├── .github └── workflows │ ├── pypi_publish.yml │ └── release_tag.yml ├── .gitignore ├── .readthedocs.yaml ├── LICENSE.md ├── README.md ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── api.rst │ ├── conf.py │ ├── index.rst │ └── usage.rst ├── setup.py └── slskd_api ├── __init__.py ├── apis ├── __init__.py ├── application.py ├── base.py ├── conversations.py ├── logs.py ├── options.py ├── public_chat.py ├── relay.py ├── rooms.py ├── searches.py ├── server.py ├── session.py ├── shares.py ├── transfers.py └── users.py └── client.py /.github/workflows/pypi_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 | push: 13 | tags: 14 | - 'v*' 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | deploy: 21 | 22 | runs-on: ubuntu-latest 23 | environment: pypi_publish 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Set up Python 27 | uses: actions/setup-python@v3 28 | with: 29 | python-version: '3.x' 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install build 34 | - name: Build package 35 | run: python -m build 36 | - name: Publish package 37 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 38 | with: 39 | #user: __token__ 40 | password: ${{ secrets.PYPI_API_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/release_tag.yml: -------------------------------------------------------------------------------- 1 | name: "tagged-release" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | tagged-release: 10 | name: "Tagged Release" 11 | runs-on: "ubuntu-latest" 12 | 13 | steps: 14 | # ... 15 | - name: "Build & test" 16 | run: | 17 | echo "done!" 18 | 19 | - uses: "marvinpinto/action-automatic-releases@latest" 20 | with: 21 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 22 | prerelease: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | 3 | .eggs 4 | *.egg-info 5 | .hypothesis 6 | .vscode 7 | 8 | build 9 | dist 10 | 11 | slskd.yml 12 | test.py -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/source/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | python: 21 | install: 22 | - requirements: docs/requirements.txt -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # GNU AFFERO GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 19 November 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | ## Preamble 12 | 13 | The GNU Affero General Public License is a free, copyleft license for 14 | software and other kinds of works, specifically designed to ensure 15 | cooperation with the community in the case of network server software. 16 | 17 | The licenses for most software and other practical works are designed 18 | to take away your freedom to share and change the works. By contrast, 19 | our General Public Licenses are intended to guarantee your freedom to 20 | share and change all versions of a program--to make sure it remains 21 | free software for all its users. 22 | 23 | When we speak of free software, we are referring to freedom, not 24 | price. Our General Public Licenses are designed to make sure that you 25 | have the freedom to distribute copies of free software (and charge for 26 | them if you wish), that you receive source code or can get it if you 27 | want it, that you can change the software or use pieces of it in new 28 | free programs, and that you know you can do these things. 29 | 30 | Developers that use our General Public Licenses protect your rights 31 | with two steps: (1) assert copyright on the software, and (2) offer 32 | you this License which gives you legal permission to copy, distribute 33 | and/or modify the software. 34 | 35 | A secondary benefit of defending all users' freedom is that 36 | improvements made in alternate versions of the program, if they 37 | receive widespread use, become available for other developers to 38 | incorporate. Many developers of free software are heartened and 39 | encouraged by the resulting cooperation. However, in the case of 40 | software used on network servers, this result may fail to come about. 41 | The GNU General Public License permits making a modified version and 42 | letting the public access it on a server without ever releasing its 43 | source code to the public. 44 | 45 | The GNU Affero General Public License is designed specifically to 46 | ensure that, in such cases, the modified source code becomes available 47 | to the community. It requires the operator of a network server to 48 | provide the source code of the modified version running there to the 49 | users of that server. Therefore, public use of a modified version, on 50 | a publicly accessible server, gives the public access to the source 51 | code of the modified version. 52 | 53 | An older license, called the Affero General Public License and 54 | published by Affero, was designed to accomplish similar goals. This is 55 | a different license, not a version of the Affero GPL, but Affero has 56 | released a new version of the Affero GPL which permits relicensing 57 | under this license. 58 | 59 | The precise terms and conditions for copying, distribution and 60 | modification follow. 61 | 62 | ## TERMS AND CONDITIONS 63 | 64 | ### 0. Definitions. 65 | 66 | "This License" refers to version 3 of the GNU Affero General Public 67 | License. 68 | 69 | "Copyright" also means copyright-like laws that apply to other kinds 70 | of works, such as semiconductor masks. 71 | 72 | "The Program" refers to any copyrightable work licensed under this 73 | License. Each licensee is addressed as "you". "Licensees" and 74 | "recipients" may be individuals or organizations. 75 | 76 | To "modify" a work means to copy from or adapt all or part of the work 77 | in a fashion requiring copyright permission, other than the making of 78 | an exact copy. The resulting work is called a "modified version" of 79 | the earlier work or a work "based on" the earlier work. 80 | 81 | A "covered work" means either the unmodified Program or a work based 82 | on the Program. 83 | 84 | To "propagate" a work means to do anything with it that, without 85 | permission, would make you directly or secondarily liable for 86 | infringement under applicable copyright law, except executing it on a 87 | computer or modifying a private copy. Propagation includes copying, 88 | distribution (with or without modification), making available to the 89 | public, and in some countries other activities as well. 90 | 91 | To "convey" a work means any kind of propagation that enables other 92 | parties to make or receive copies. Mere interaction with a user 93 | through a computer network, with no transfer of a copy, is not 94 | conveying. 95 | 96 | An interactive user interface displays "Appropriate Legal Notices" to 97 | the extent that it includes a convenient and prominently visible 98 | feature that (1) displays an appropriate copyright notice, and (2) 99 | tells the user that there is no warranty for the work (except to the 100 | extent that warranties are provided), that licensees may convey the 101 | work under this License, and how to view a copy of this License. If 102 | the interface presents a list of user commands or options, such as a 103 | menu, a prominent item in the list meets this criterion. 104 | 105 | ### 1. Source Code. 106 | 107 | The "source code" for a work means the preferred form of the work for 108 | making modifications to it. "Object code" means any non-source form of 109 | a work. 110 | 111 | A "Standard Interface" means an interface that either is an official 112 | standard defined by a recognized standards body, or, in the case of 113 | interfaces specified for a particular programming language, one that 114 | is widely used among developers working in that language. 115 | 116 | The "System Libraries" of an executable work include anything, other 117 | than the work as a whole, that (a) is included in the normal form of 118 | packaging a Major Component, but which is not part of that Major 119 | Component, and (b) serves only to enable use of the work with that 120 | Major Component, or to implement a Standard Interface for which an 121 | implementation is available to the public in source code form. A 122 | "Major Component", in this context, means a major essential component 123 | (kernel, window system, and so on) of the specific operating system 124 | (if any) on which the executable work runs, or a compiler used to 125 | produce the work, or an object code interpreter used to run it. 126 | 127 | The "Corresponding Source" for a work in object code form means all 128 | the source code needed to generate, install, and (for an executable 129 | work) run the object code and to modify the work, including scripts to 130 | control those activities. However, it does not include the work's 131 | System Libraries, or general-purpose tools or generally available free 132 | programs which are used unmodified in performing those activities but 133 | which are not part of the work. For example, Corresponding Source 134 | includes interface definition files associated with source files for 135 | the work, and the source code for shared libraries and dynamically 136 | linked subprograms that the work is specifically designed to require, 137 | such as by intimate data communication or control flow between those 138 | subprograms and other parts of the work. 139 | 140 | The Corresponding Source need not include anything that users can 141 | regenerate automatically from other parts of the Corresponding Source. 142 | 143 | The Corresponding Source for a work in source code form is that same 144 | work. 145 | 146 | ### 2. Basic Permissions. 147 | 148 | All rights granted under this License are granted for the term of 149 | copyright on the Program, and are irrevocable provided the stated 150 | conditions are met. This License explicitly affirms your unlimited 151 | permission to run the unmodified Program. The output from running a 152 | covered work is covered by this License only if the output, given its 153 | content, constitutes a covered work. This License acknowledges your 154 | rights of fair use or other equivalent, as provided by copyright law. 155 | 156 | You may make, run and propagate covered works that you do not convey, 157 | without conditions so long as your license otherwise remains in force. 158 | You may convey covered works to others for the sole purpose of having 159 | them make modifications exclusively for you, or provide you with 160 | facilities for running those works, provided that you comply with the 161 | terms of this License in conveying all material for which you do not 162 | control copyright. Those thus making or running the covered works for 163 | you must do so exclusively on your behalf, under your direction and 164 | control, on terms that prohibit them from making any copies of your 165 | copyrighted material outside their relationship with you. 166 | 167 | Conveying under any other circumstances is permitted solely under the 168 | conditions stated below. Sublicensing is not allowed; section 10 makes 169 | it unnecessary. 170 | 171 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 172 | 173 | No covered work shall be deemed part of an effective technological 174 | measure under any applicable law fulfilling obligations under article 175 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 176 | similar laws prohibiting or restricting circumvention of such 177 | measures. 178 | 179 | When you convey a covered work, you waive any legal power to forbid 180 | circumvention of technological measures to the extent such 181 | circumvention is effected by exercising rights under this License with 182 | respect to the covered work, and you disclaim any intention to limit 183 | operation or modification of the work as a means of enforcing, against 184 | the work's users, your or third parties' legal rights to forbid 185 | circumvention of technological measures. 186 | 187 | ### 4. Conveying Verbatim Copies. 188 | 189 | You may convey verbatim copies of the Program's source code as you 190 | receive it, in any medium, provided that you conspicuously and 191 | appropriately publish on each copy an appropriate copyright notice; 192 | keep intact all notices stating that this License and any 193 | non-permissive terms added in accord with section 7 apply to the code; 194 | keep intact all notices of the absence of any warranty; and give all 195 | recipients a copy of this License along with the Program. 196 | 197 | You may charge any price or no price for each copy that you convey, 198 | and you may offer support or warranty protection for a fee. 199 | 200 | ### 5. Conveying Modified Source Versions. 201 | 202 | You may convey a work based on the Program, or the modifications to 203 | produce it from the Program, in the form of source code under the 204 | terms of section 4, provided that you also meet all of these 205 | conditions: 206 | 207 | - a) The work must carry prominent notices stating that you modified 208 | it, and giving a relevant date. 209 | - b) The work must carry prominent notices stating that it is 210 | released under this License and any conditions added under 211 | section 7. This requirement modifies the requirement in section 4 212 | to "keep intact all notices". 213 | - c) You must license the entire work, as a whole, under this 214 | License to anyone who comes into possession of a copy. This 215 | License will therefore apply, along with any applicable section 7 216 | additional terms, to the whole of the work, and all its parts, 217 | regardless of how they are packaged. This License gives no 218 | permission to license the work in any other way, but it does not 219 | invalidate such permission if you have separately received it. 220 | - d) If the work has interactive user interfaces, each must display 221 | Appropriate Legal Notices; however, if the Program has interactive 222 | interfaces that do not display Appropriate Legal Notices, your 223 | work need not make them do so. 224 | 225 | A compilation of a covered work with other separate and independent 226 | works, which are not by their nature extensions of the covered work, 227 | and which are not combined with it such as to form a larger program, 228 | in or on a volume of a storage or distribution medium, is called an 229 | "aggregate" if the compilation and its resulting copyright are not 230 | used to limit the access or legal rights of the compilation's users 231 | beyond what the individual works permit. Inclusion of a covered work 232 | in an aggregate does not cause this License to apply to the other 233 | parts of the aggregate. 234 | 235 | ### 6. Conveying Non-Source Forms. 236 | 237 | You may convey a covered work in object code form under the terms of 238 | sections 4 and 5, provided that you also convey the machine-readable 239 | Corresponding Source under the terms of this License, in one of these 240 | ways: 241 | 242 | - a) Convey the object code in, or embodied in, a physical product 243 | (including a physical distribution medium), accompanied by the 244 | Corresponding Source fixed on a durable physical medium 245 | customarily used for software interchange. 246 | - b) Convey the object code in, or embodied in, a physical product 247 | (including a physical distribution medium), accompanied by a 248 | written offer, valid for at least three years and valid for as 249 | long as you offer spare parts or customer support for that product 250 | model, to give anyone who possesses the object code either (1) a 251 | copy of the Corresponding Source for all the software in the 252 | product that is covered by this License, on a durable physical 253 | medium customarily used for software interchange, for a price no 254 | more than your reasonable cost of physically performing this 255 | conveying of source, or (2) access to copy the Corresponding 256 | Source from a network server at no charge. 257 | - c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | - d) Convey the object code by offering access from a designated 263 | place (gratis or for a charge), and offer equivalent access to the 264 | Corresponding Source in the same way through the same place at no 265 | further charge. You need not require recipients to copy the 266 | Corresponding Source along with the object code. If the place to 267 | copy the object code is a network server, the Corresponding Source 268 | may be on a different server (operated by you or a third party) 269 | that supports equivalent copying facilities, provided you maintain 270 | clear directions next to the object code saying where to find the 271 | Corresponding Source. Regardless of what server hosts the 272 | Corresponding Source, you remain obligated to ensure that it is 273 | available for as long as needed to satisfy these requirements. 274 | - e) Convey the object code using peer-to-peer transmission, 275 | provided you inform other peers where the object code and 276 | Corresponding Source of the work are being offered to the general 277 | public at no charge under subsection 6d. 278 | 279 | A separable portion of the object code, whose source code is excluded 280 | from the Corresponding Source as a System Library, need not be 281 | included in conveying the object code work. 282 | 283 | A "User Product" is either (1) a "consumer product", which means any 284 | tangible personal property which is normally used for personal, 285 | family, or household purposes, or (2) anything designed or sold for 286 | incorporation into a dwelling. In determining whether a product is a 287 | consumer product, doubtful cases shall be resolved in favor of 288 | coverage. For a particular product received by a particular user, 289 | "normally used" refers to a typical or common use of that class of 290 | product, regardless of the status of the particular user or of the way 291 | in which the particular user actually uses, or expects or is expected 292 | to use, the product. A product is a consumer product regardless of 293 | whether the product has substantial commercial, industrial or 294 | non-consumer uses, unless such uses represent the only significant 295 | mode of use of the product. 296 | 297 | "Installation Information" for a User Product means any methods, 298 | procedures, authorization keys, or other information required to 299 | install and execute modified versions of a covered work in that User 300 | Product from a modified version of its Corresponding Source. The 301 | information must suffice to ensure that the continued functioning of 302 | the modified object code is in no case prevented or interfered with 303 | solely because modification has been made. 304 | 305 | If you convey an object code work under this section in, or with, or 306 | specifically for use in, a User Product, and the conveying occurs as 307 | part of a transaction in which the right of possession and use of the 308 | User Product is transferred to the recipient in perpetuity or for a 309 | fixed term (regardless of how the transaction is characterized), the 310 | Corresponding Source conveyed under this section must be accompanied 311 | by the Installation Information. But this requirement does not apply 312 | if neither you nor any third party retains the ability to install 313 | modified object code on the User Product (for example, the work has 314 | been installed in ROM). 315 | 316 | The requirement to provide Installation Information does not include a 317 | requirement to continue to provide support service, warranty, or 318 | updates for a work that has been modified or installed by the 319 | recipient, or for the User Product in which it has been modified or 320 | installed. Access to a network may be denied when the modification 321 | itself materially and adversely affects the operation of the network 322 | or violates the rules and protocols for communication across the 323 | network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | ### 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders 351 | of that material) supplement the terms of this License with terms: 352 | 353 | - a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | - b) Requiring preservation of specified reasonable legal notices or 356 | author attributions in that material or in the Appropriate Legal 357 | Notices displayed by works containing it; or 358 | - c) Prohibiting misrepresentation of the origin of that material, 359 | or requiring that modified versions of such material be marked in 360 | reasonable ways as different from the original version; or 361 | - d) Limiting the use for publicity purposes of names of licensors 362 | or authors of the material; or 363 | - e) Declining to grant rights under trademark law for use of some 364 | trade names, trademarks, or service marks; or 365 | - f) Requiring indemnification of licensors and authors of that 366 | material by anyone who conveys the material (or modified versions 367 | of it) with contractual assumptions of liability to the recipient, 368 | for any liability that these contractual assumptions directly 369 | impose on those licensors and authors. 370 | 371 | All other non-permissive additional terms are considered "further 372 | restrictions" within the meaning of section 10. If the Program as you 373 | received it, or any part of it, contains a notice stating that it is 374 | governed by this License along with a term that is a further 375 | restriction, you may remove that term. If a license document contains 376 | a further restriction but permits relicensing or conveying under this 377 | License, you may add to a covered work material governed by the terms 378 | of that license document, provided that the further restriction does 379 | not survive such relicensing or conveying. 380 | 381 | If you add terms to a covered work in accord with this section, you 382 | must place, in the relevant source files, a statement of the 383 | additional terms that apply to those files, or a notice indicating 384 | where to find the applicable terms. 385 | 386 | Additional terms, permissive or non-permissive, may be stated in the 387 | form of a separately written license, or stated as exceptions; the 388 | above requirements apply either way. 389 | 390 | ### 8. Termination. 391 | 392 | You may not propagate or modify a covered work except as expressly 393 | provided under this License. Any attempt otherwise to propagate or 394 | modify it is void, and will automatically terminate your rights under 395 | this License (including any patent licenses granted under the third 396 | paragraph of section 11). 397 | 398 | However, if you cease all violation of this License, then your license 399 | from a particular copyright holder is reinstated (a) provisionally, 400 | unless and until the copyright holder explicitly and finally 401 | terminates your license, and (b) permanently, if the copyright holder 402 | fails to notify you of the violation by some reasonable means prior to 403 | 60 days after the cessation. 404 | 405 | Moreover, your license from a particular copyright holder is 406 | reinstated permanently if the copyright holder notifies you of the 407 | violation by some reasonable means, this is the first time you have 408 | received notice of violation of this License (for any work) from that 409 | copyright holder, and you cure the violation prior to 30 days after 410 | your receipt of the notice. 411 | 412 | Termination of your rights under this section does not terminate the 413 | licenses of parties who have received copies or rights from you under 414 | this License. If your rights have been terminated and not permanently 415 | reinstated, you do not qualify to receive new licenses for the same 416 | material under section 10. 417 | 418 | ### 9. Acceptance Not Required for Having Copies. 419 | 420 | You are not required to accept this License in order to receive or run 421 | a copy of the Program. Ancillary propagation of a covered work 422 | occurring solely as a consequence of using peer-to-peer transmission 423 | to receive a copy likewise does not require acceptance. However, 424 | nothing other than this License grants you permission to propagate or 425 | modify any covered work. These actions infringe copyright if you do 426 | not accept this License. Therefore, by modifying or propagating a 427 | covered work, you indicate your acceptance of this License to do so. 428 | 429 | ### 10. Automatic Licensing of Downstream Recipients. 430 | 431 | Each time you convey a covered work, the recipient automatically 432 | receives a license from the original licensors, to run, modify and 433 | propagate that work, subject to this License. You are not responsible 434 | for enforcing compliance by third parties with this License. 435 | 436 | An "entity transaction" is a transaction transferring control of an 437 | organization, or substantially all assets of one, or subdividing an 438 | organization, or merging organizations. If propagation of a covered 439 | work results from an entity transaction, each party to that 440 | transaction who receives a copy of the work also receives whatever 441 | licenses to the work the party's predecessor in interest had or could 442 | give under the previous paragraph, plus a right to possession of the 443 | Corresponding Source of the work from the predecessor in interest, if 444 | the predecessor has it or can get it with reasonable efforts. 445 | 446 | You may not impose any further restrictions on the exercise of the 447 | rights granted or affirmed under this License. For example, you may 448 | not impose a license fee, royalty, or other charge for exercise of 449 | rights granted under this License, and you may not initiate litigation 450 | (including a cross-claim or counterclaim in a lawsuit) alleging that 451 | any patent claim is infringed by making, using, selling, offering for 452 | sale, or importing the Program or any portion of it. 453 | 454 | ### 11. Patents. 455 | 456 | A "contributor" is a copyright holder who authorizes use under this 457 | License of the Program or a work on which the Program is based. The 458 | work thus licensed is called the contributor's "contributor version". 459 | 460 | A contributor's "essential patent claims" are all patent claims owned 461 | or controlled by the contributor, whether already acquired or 462 | hereafter acquired, that would be infringed by some manner, permitted 463 | by this License, of making, using, or selling its contributor version, 464 | but do not include claims that would be infringed only as a 465 | consequence of further modification of the contributor version. For 466 | purposes of this definition, "control" includes the right to grant 467 | patent sublicenses in a manner consistent with the requirements of 468 | this License. 469 | 470 | Each contributor grants you a non-exclusive, worldwide, royalty-free 471 | patent license under the contributor's essential patent claims, to 472 | make, use, sell, offer for sale, import and otherwise run, modify and 473 | propagate the contents of its contributor version. 474 | 475 | In the following three paragraphs, a "patent license" is any express 476 | agreement or commitment, however denominated, not to enforce a patent 477 | (such as an express permission to practice a patent or covenant not to 478 | sue for patent infringement). To "grant" such a patent license to a 479 | party means to make such an agreement or commitment not to enforce a 480 | patent against the party. 481 | 482 | If you convey a covered work, knowingly relying on a patent license, 483 | and the Corresponding Source of the work is not available for anyone 484 | to copy, free of charge and under the terms of this License, through a 485 | publicly available network server or other readily accessible means, 486 | then you must either (1) cause the Corresponding Source to be so 487 | available, or (2) arrange to deprive yourself of the benefit of the 488 | patent license for this particular work, or (3) arrange, in a manner 489 | consistent with the requirements of this License, to extend the patent 490 | license to downstream recipients. "Knowingly relying" means you have 491 | actual knowledge that, but for the patent license, your conveying the 492 | covered work in a country, or your recipient's use of the covered work 493 | in a country, would infringe one or more identifiable patents in that 494 | country that you have reason to believe are valid. 495 | 496 | If, pursuant to or in connection with a single transaction or 497 | arrangement, you convey, or propagate by procuring conveyance of, a 498 | covered work, and grant a patent license to some of the parties 499 | receiving the covered work authorizing them to use, propagate, modify 500 | or convey a specific copy of the covered work, then the patent license 501 | you grant is automatically extended to all recipients of the covered 502 | work and works based on it. 503 | 504 | A patent license is "discriminatory" if it does not include within the 505 | scope of its coverage, prohibits the exercise of, or is conditioned on 506 | the non-exercise of one or more of the rights that are specifically 507 | granted under this License. You may not convey a covered work if you 508 | are a party to an arrangement with a third party that is in the 509 | business of distributing software, under which you make payment to the 510 | third party based on the extent of your activity of conveying the 511 | work, and under which the third party grants, to any of the parties 512 | who would receive the covered work from you, a discriminatory patent 513 | license (a) in connection with copies of the covered work conveyed by 514 | you (or copies made from those copies), or (b) primarily for and in 515 | connection with specific products or compilations that contain the 516 | covered work, unless you entered into that arrangement, or that patent 517 | license was granted, prior to 28 March 2007. 518 | 519 | Nothing in this License shall be construed as excluding or limiting 520 | any implied license or other defenses to infringement that may 521 | otherwise be available to you under applicable patent law. 522 | 523 | ### 12. No Surrender of Others' Freedom. 524 | 525 | If conditions are imposed on you (whether by court order, agreement or 526 | otherwise) that contradict the conditions of this License, they do not 527 | excuse you from the conditions of this License. If you cannot convey a 528 | covered work so as to satisfy simultaneously your obligations under 529 | this License and any other pertinent obligations, then as a 530 | consequence you may not convey it at all. For example, if you agree to 531 | terms that obligate you to collect a royalty for further conveying 532 | from those to whom you convey the Program, the only way you could 533 | satisfy both those terms and this License would be to refrain entirely 534 | from conveying the Program. 535 | 536 | ### 13. Remote Network Interaction; Use with the GNU General Public License. 537 | 538 | Notwithstanding any other provision of this License, if you modify the 539 | Program, your modified version must prominently offer all users 540 | interacting with it remotely through a computer network (if your 541 | version supports such interaction) an opportunity to receive the 542 | Corresponding Source of your version by providing access to the 543 | Corresponding Source from a network server at no charge, through some 544 | standard or customary means of facilitating copying of software. This 545 | Corresponding Source shall include the Corresponding Source for any 546 | work covered by version 3 of the GNU General Public License that is 547 | incorporated pursuant to the following paragraph. 548 | 549 | Notwithstanding any other provision of this License, you have 550 | permission to link or combine any covered work with a work licensed 551 | under version 3 of the GNU General Public License into a single 552 | combined work, and to convey the resulting work. The terms of this 553 | License will continue to apply to the part which is the covered work, 554 | but the work with which it is combined will remain governed by version 555 | 3 of the GNU General Public License. 556 | 557 | ### 14. Revised Versions of this License. 558 | 559 | The Free Software Foundation may publish revised and/or new versions 560 | of the GNU Affero General Public License from time to time. Such new 561 | versions will be similar in spirit to the present version, but may 562 | differ in detail to address new problems or concerns. 563 | 564 | Each version is given a distinguishing version number. If the Program 565 | specifies that a certain numbered version of the GNU Affero General 566 | Public License "or any later version" applies to it, you have the 567 | option of following the terms and conditions either of that numbered 568 | version or of any later version published by the Free Software 569 | Foundation. If the Program does not specify a version number of the 570 | GNU Affero General Public License, you may choose any version ever 571 | published by the Free Software Foundation. 572 | 573 | If the Program specifies that a proxy can decide which future versions 574 | of the GNU Affero General Public License can be used, that proxy's 575 | public statement of acceptance of a version permanently authorizes you 576 | to choose that version for the Program. 577 | 578 | Later license versions may give you additional or different 579 | permissions. However, no additional obligations are imposed on any 580 | author or copyright holder as a result of your choosing to follow a 581 | later version. 582 | 583 | ### 15. Disclaimer of Warranty. 584 | 585 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 586 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 587 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT 588 | WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT 589 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 590 | A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 591 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE 592 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR 593 | CORRECTION. 594 | 595 | ### 16. Limitation of Liability. 596 | 597 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 598 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR 599 | CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 600 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES 601 | ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT 602 | NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR 603 | LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM 604 | TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER 605 | PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 606 | 607 | ### 17. Interpretation of Sections 15 and 16. 608 | 609 | If the disclaimer of warranty and limitation of liability provided 610 | above cannot be given local legal effect according to their terms, 611 | reviewing courts shall apply local law that most closely approximates 612 | an absolute waiver of all civil liability in connection with the 613 | Program, unless a warranty or assumption of liability accompanies a 614 | copy of the Program in return for a fee. 615 | 616 | END OF TERMS AND CONDITIONS 617 | 618 | ## How to Apply These Terms to Your New Programs 619 | 620 | If you develop a new program, and you want it to be of the greatest 621 | possible use to the public, the best way to achieve this is to make it 622 | free software which everyone can redistribute and change under these 623 | terms. 624 | 625 | To do so, attach the following notices to the program. It is safest to 626 | attach them to the start of each source file to most effectively state 627 | the exclusion of warranty; and each file should have at least the 628 | "copyright" line and a pointer to where the full notice is found. 629 | 630 | 631 | Copyright (C) 632 | 633 | This program is free software: you can redistribute it and/or modify 634 | it under the terms of the GNU Affero General Public License as 635 | published by the Free Software Foundation, either version 3 of the 636 | License, or (at your option) any later version. 637 | 638 | This program is distributed in the hope that it will be useful, 639 | but WITHOUT ANY WARRANTY; without even the implied warranty of 640 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 641 | GNU Affero General Public License for more details. 642 | 643 | You should have received a copy of the GNU Affero General Public License 644 | along with this program. If not, see . 645 | 646 | Also add information on how to contact you by electronic and paper 647 | mail. 648 | 649 | If your software can interact with users remotely through a computer 650 | network, you should also make sure that it provides a way for users to 651 | get its source. For example, if your program is a web application, its 652 | interface could display a "Source" link that leads users to an archive 653 | of the code. There are many ways you could offer source, and different 654 | solutions will be better for different programs; see section 13 for 655 | the specific requirements. 656 | 657 | You should also get your employer (if you work as a programmer) or 658 | school, if any, to sign a "copyright disclaimer" for the program, if 659 | necessary. For more information on this, and how to apply and follow 660 | the GNU AGPL, see . 661 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slskd-api 2 | 3 | ## Goal 4 | This project provides a python API for [slskd](https://github.com/slskd/slskd). 5 | 6 | A comprehensive python API should help improve integration and increase slskd adoption by enabling rapid development of various software, such as: 7 | - [ ] Desktop/Mobile Clients 8 | - [ ] [beets](https://github.com/beetbox/beets) plugin(s) 9 | - [ ] [headphones](https://github.com/rembo10/headphones) integration 10 | 11 | ## Installation 12 | The package is availaible on [pypi](https://pypi.org/project/slskd-api/): 13 | ``` 14 | pip install slskd-api 15 | ``` 16 | 17 | ## Usage 18 | Create a `slskd` instance with the following: 19 | ``` 20 | import slskd_api 21 | slskd = slskd_api.SlskdClient(host, api_key, url_base) 22 | ``` 23 | Then you'll be able to access all API methods: 24 | ``` 25 | app_status = slskd.application.state() 26 | available_rooms = slskd.rooms.get_all() 27 | ``` 28 | See the [doc](https://slskd-api.readthedocs.io/) for further details. 29 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_autodoc_typehints 2 | sphinx-rtd-theme -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | client.py 5 | _________ 6 | 7 | .. autoclass:: slskd_api.SlskdClient 8 | :members: 9 | 10 | .. autoclass:: slskd_api.MetricsApi 11 | :members: 12 | 13 | apis/application.py 14 | ______________ 15 | 16 | .. autoclass:: slskd_api.apis.ApplicationApi 17 | :members: 18 | 19 | apis/conversations.py 20 | ______________ 21 | 22 | .. autoclass:: slskd_api.apis.ConversationsApi 23 | :members: 24 | 25 | apis/logs.py 26 | ______________ 27 | 28 | .. autoclass:: slskd_api.apis.LogsApi 29 | :members: 30 | 31 | apis/options.py 32 | ______________ 33 | 34 | .. autoclass:: slskd_api.apis.OptionsApi 35 | :members: 36 | 37 | apis/public_chat.py 38 | ______________ 39 | 40 | .. autoclass:: slskd_api.apis.PublicChatApi 41 | :members: 42 | 43 | apis/relay.py 44 | ______________ 45 | 46 | .. autoclass:: slskd_api.apis.RelayApi 47 | :members: 48 | 49 | apis/rooms.py 50 | ______________ 51 | 52 | .. autoclass:: slskd_api.apis.RoomsApi 53 | :members: 54 | 55 | apis/searches.py 56 | ______________ 57 | 58 | .. autoclass:: slskd_api.apis.SearchesApi 59 | :members: 60 | 61 | apis/server.py 62 | ______________ 63 | 64 | .. autoclass:: slskd_api.apis.ServerApi 65 | :members: 66 | 67 | apis/session.py 68 | ______________ 69 | 70 | .. autoclass:: slskd_api.apis.SessionApi 71 | :members: 72 | 73 | apis/shares.py 74 | ______________ 75 | 76 | .. autoclass:: slskd_api.apis.SharesApi 77 | :members: 78 | 79 | apis/transfers.py 80 | ______________ 81 | 82 | .. autoclass:: slskd_api.apis.TransfersApi 83 | :members: 84 | 85 | apis/users.py 86 | ______________ 87 | 88 | .. autoclass:: slskd_api.apis.UsersApi 89 | :members: -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | import pathlib 3 | import sys 4 | sys.path.insert(0, pathlib.Path(__file__).parents[2].resolve().as_posix()) 5 | 6 | # -- Project information 7 | 8 | project = 'slskd-python-api' 9 | copyright = '2023, bigoulours' 10 | author = 'bigoulours' 11 | 12 | # -- General configuration 13 | 14 | extensions = [ 15 | 'sphinx.ext.duration', 16 | 'sphinx.ext.doctest', 17 | 'sphinx.ext.autodoc', 18 | 'sphinx_autodoc_typehints', 19 | 'sphinx.ext.autosummary', 20 | 'sphinx.ext.intersphinx', 21 | ] 22 | 23 | intersphinx_mapping = { 24 | 'python': ('https://docs.python.org/3/', None), 25 | 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), 26 | } 27 | intersphinx_disabled_domains = ['std'] 28 | 29 | templates_path = ['_templates'] 30 | 31 | add_module_names = False 32 | 33 | autodoc_member_order = 'bysource' 34 | 35 | # autodoc_typehints="none" 36 | # typehints_document_rtype = False 37 | typehints_use_rtype = False 38 | typehints_use_signature = True 39 | #typehints_use_signature_return = True 40 | 41 | # -- Options for HTML output 42 | html_theme = 'sphinx_rtd_theme' 43 | 44 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to slskd-python-api's documentation! 2 | ============================================ 3 | 4 | **slskd-api** is a Python library to interact with `slskd `_. 5 | 6 | Check out the :doc:`usage` section for further information, including 7 | installation and basic examples. 8 | 9 | .. note:: 10 | 11 | This project is under active development. 12 | 13 | Contents 14 | -------- 15 | 16 | .. toctree:: 17 | 18 | usage 19 | api 20 | -------------------------------------------------------------------------------- /docs/source/usage.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | .. _installation: 5 | 6 | Installation 7 | ------------ 8 | 9 | To use slskd-api, first install it using pip: 10 | 11 | .. code-block:: console 12 | 13 | (.venv) $ pip install slskd-api 14 | 15 | Send requests to the server 16 | --------------------------- 17 | 18 | Once the module is installed, you can create an instance of slskd client with the following code, 19 | granted an API-Key was setup in your `slskd configuration file `_. 20 | 21 | .. code-block:: console 22 | 23 | import slskd_api 24 | slskd = slskd_api.SlskdClient(host, api_key, url_base) 25 | 26 | From there all APIs (with the exception of :py:class:`~slskd_api.MetricsApi`) 27 | will be accessible through ``slskd``: 28 | 29 | .. code-block:: console 30 | 31 | app_status = slskd.application.state() 32 | available_rooms = slskd.rooms.get_all() 33 | 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='slskd-api', 5 | setuptools_git_versioning={ 6 | "template": "{tag}", 7 | "dev_template": "{tag}", 8 | "dirty_template": "{tag}" 9 | }, 10 | setup_requires = ["setuptools-git-versioning"], 11 | packages=find_packages(), 12 | long_description=open('README.md').read(), 13 | long_description_content_type='text/markdown', 14 | install_requires=[ 15 | 'requests>=2.25.1', 16 | ], 17 | project_urls={ 18 | 'Documentation': 'https://slskd-api.readthedocs.io', 19 | 'Source': 'https://github.com/bigoulours/slskd-python-api', 20 | 'Funding': 'https://liberapay.com/bigoulours/donate' 21 | }, 22 | author='bigoulours', 23 | description = 'API Wrapper to interact with slskd', 24 | python_requires='>=3.7', 25 | license='GNU Affero General Public License v3.0', 26 | ) -------------------------------------------------------------------------------- /slskd_api/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .client import SlskdClient, MetricsApi 17 | 18 | __all__ = ('SlskdClient', 'MetricsApi') -------------------------------------------------------------------------------- /slskd_api/apis/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .application import ApplicationApi 17 | from .conversations import ConversationsApi 18 | from .logs import LogsApi 19 | from .options import OptionsApi 20 | from .public_chat import PublicChatApi 21 | from .relay import RelayApi 22 | from .rooms import RoomsApi 23 | from .searches import SearchesApi 24 | from .server import ServerApi 25 | from .session import SessionApi 26 | from .shares import SharesApi 27 | from .transfers import TransfersApi 28 | from .users import UsersApi 29 | 30 | __all__ = ( 31 | 'ApplicationApi', 32 | 'ConversationsApi', 33 | 'LogsApi', 34 | 'OptionsApi', 35 | 'PublicChatApi', 36 | 'RelayApi', 37 | 'RoomsApi', 38 | 'SearchesApi', 39 | 'ServerApi', 40 | 'SessionApi', 41 | 'SharesApi', 42 | 'TransfersApi', 43 | 'UsersApi' 44 | ) 45 | -------------------------------------------------------------------------------- /slskd_api/apis/application.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class ApplicationApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Application API. 21 | """ 22 | 23 | def state(self) -> dict: 24 | """ 25 | Gets the current state of the application. 26 | """ 27 | url = self.api_url + '/application' 28 | response = self.session.get(url) 29 | return response.json() 30 | 31 | 32 | def stop(self) -> bool: 33 | """ 34 | Stops the application. Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 35 | 36 | :return: True if successful. 37 | """ 38 | url = self.api_url + '/application' 39 | response = self.session.delete(url) 40 | return response.ok 41 | 42 | 43 | def restart(self) -> bool: 44 | """ 45 | Restarts the application. Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 46 | 47 | :return: True if successful. 48 | """ 49 | url = self.api_url + '/application' 50 | response = self.session.put(url) 51 | return response.ok 52 | 53 | 54 | def version(self) -> str: 55 | """ 56 | Gets the current application version. 57 | """ 58 | url = self.api_url + '/application/version' 59 | response = self.session.get(url) 60 | return response.json() 61 | 62 | 63 | def check_updates(self, forceCheck: bool = False) -> dict: 64 | """ 65 | Checks for updates. 66 | """ 67 | url = self.api_url + '/application/version/latest' 68 | params = dict( 69 | forceCheck=forceCheck 70 | ) 71 | response = self.session.get(url, params=params) 72 | return response.json() 73 | 74 | 75 | def gc(self) -> bool: 76 | """ 77 | Forces garbage collection. 78 | 79 | :return: True if successful. 80 | """ 81 | url = self.api_url + '/application/gc' 82 | response = self.session.post(url) 83 | return response.ok 84 | 85 | 86 | # Not supposed to be part of the external API 87 | # More info in the Github discussion: https://github.com/slskd/slskd/discussions/910 88 | # def dump(self): 89 | # url = self.api_url + '/application/dump' 90 | # response = self.session.get(url) 91 | # return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import requests 17 | from urllib.parse import quote 18 | 19 | class BaseApi: 20 | """ 21 | Base class where api-url and headers are set for all requests. 22 | """ 23 | 24 | def __init__(self, api_url: str, session: requests.Session): 25 | self.api_url = api_url 26 | self.session = session -------------------------------------------------------------------------------- /slskd_api/apis/conversations.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class ConversationsApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Conversations API. 21 | """ 22 | 23 | def acknowledge(self, username: str, id: int) -> bool: 24 | """ 25 | Acknowledges the given message id for the given username. 26 | 27 | :return: True if successful. 28 | """ 29 | url = self.api_url + f'/conversations/{quote(username)}/{id}' 30 | response = self.session.put(url) 31 | return response.ok 32 | 33 | 34 | def acknowledge_all(self, username: str) -> bool: 35 | """ 36 | Acknowledges all messages from the given username. 37 | 38 | :return: True if successful. 39 | """ 40 | url = self.api_url + f'/conversations/{quote(username)}' 41 | response = self.session.put(url) 42 | return response.ok 43 | 44 | 45 | def delete(self, username: str) -> bool: 46 | """ 47 | Closes the conversation associated with the given username. 48 | 49 | :return: True if successful. 50 | """ 51 | url = self.api_url + f'/conversations/{quote(username)}' 52 | response = self.session.delete(url) 53 | return response.ok 54 | 55 | 56 | def get(self, username: str, includeMessages: bool = True) -> dict: 57 | """ 58 | Gets the conversation associated with the specified username. 59 | """ 60 | url = self.api_url + f'/conversations/{quote(username)}' 61 | params = dict( 62 | includeMessages=includeMessages 63 | ) 64 | response = self.session.get(url, params=params) 65 | return response.json() 66 | 67 | 68 | def send(self, username: str, message: str) -> bool: 69 | """ 70 | Sends a private message to the specified username. 71 | 72 | :return: True if successful. 73 | """ 74 | url = self.api_url + f'/conversations/{quote(username)}' 75 | response = self.session.post(url, json=message) 76 | return response.ok 77 | 78 | 79 | def get_all(self, includeInactive: bool = False, unAcknowledgedOnly : bool = False) -> list: 80 | """ 81 | Gets all active conversations. 82 | """ 83 | url = self.api_url + '/conversations' 84 | params = dict( 85 | includeInactive=includeInactive, 86 | unAcknowledgedOnly=unAcknowledgedOnly 87 | ) 88 | response = self.session.get(url, params=params) 89 | return response.json() 90 | 91 | 92 | def get_messages(self, username: str, unAcknowledgedOnly : bool = False) -> list: 93 | """ 94 | Gets all messages associated with the specified username. 95 | """ 96 | url = self.api_url + f'/conversations/{quote(username)}/messages' 97 | params = dict( 98 | username=username, 99 | unAcknowledgedOnly=unAcknowledgedOnly 100 | ) 101 | response = self.session.get(url, params=params) 102 | return response.json() 103 | -------------------------------------------------------------------------------- /slskd_api/apis/logs.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class LogsApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Logs API. 21 | """ 22 | 23 | def get(self) -> list: 24 | """ 25 | Gets the last few application logs. 26 | """ 27 | url = self.api_url + '/logs' 28 | response = self.session.get(url) 29 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/options.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class OptionsApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Options API. 21 | """ 22 | 23 | def get(self) -> dict: 24 | """ 25 | Gets the current application options. 26 | """ 27 | url = self.api_url + '/options' 28 | response = self.session.get(url) 29 | return response.json() 30 | 31 | 32 | def get_startup(self) -> dict: 33 | """ 34 | Gets the application options provided at startup. 35 | """ 36 | url = self.api_url + '/options/startup' 37 | response = self.session.get(url) 38 | return response.json() 39 | 40 | 41 | def debug(self) -> str: 42 | """ 43 | Gets the debug view of the current application options. 44 | debug and remote_configuration must be set to true. 45 | Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 46 | """ 47 | url = self.api_url + '/options/debug' 48 | response = self.session.get(url) 49 | return response.json() 50 | 51 | 52 | def yaml_location(self) -> str: 53 | """ 54 | Gets the path of the yaml config file. remote_configuration must be set to true. 55 | Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 56 | """ 57 | url = self.api_url + '/options/yaml/location' 58 | response = self.session.get(url) 59 | return response.json() 60 | 61 | 62 | def download_yaml(self) -> str: 63 | """ 64 | Gets the content of the yaml config file as text. remote_configuration must be set to true. 65 | Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 66 | """ 67 | url = self.api_url + '/options/yaml' 68 | response = self.session.get(url) 69 | return response.json() 70 | 71 | 72 | def upload_yaml(self, yaml_content: str) -> bool: 73 | """ 74 | Sets the content of the yaml config file. remote_configuration must be set to true. 75 | Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 76 | 77 | :return: True if successful. 78 | """ 79 | url = self.api_url + '/options/yaml' 80 | response = self.session.post(url, json=yaml_content) 81 | return response.ok 82 | 83 | 84 | def validate_yaml(self, yaml_content: str) -> str: 85 | """ 86 | Validates the provided yaml string. remote_configuration must be set to true. 87 | Only works with token (usr/pwd login). 'Unauthorized' with API-Key. 88 | 89 | :return: Empty string if validation successful. Error message otherwise. 90 | """ 91 | url = self.api_url + '/options/yaml/validate' 92 | response = self.session.post(url, json=yaml_content) 93 | return response.text -------------------------------------------------------------------------------- /slskd_api/apis/public_chat.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class PublicChatApi(BaseApi): 19 | """ 20 | [UNTESTED] This class contains the methods to interact with the PublicChat API. 21 | """ 22 | 23 | def start(self) -> bool: 24 | """ 25 | Starts public chat. 26 | 27 | :return: True if successful. 28 | """ 29 | url = self.api_url + '/publicchat' 30 | response = self.session.post(url) 31 | return response.ok 32 | 33 | 34 | def stop(self) -> bool: 35 | """ 36 | Stops public chat. 37 | 38 | :return: True if successful. 39 | """ 40 | url = self.api_url + '/publicchat' 41 | response = self.session.delete(url) 42 | return response.ok 43 | -------------------------------------------------------------------------------- /slskd_api/apis/relay.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class RelayApi(BaseApi): 19 | """ 20 | [UNTESTED] This class contains the methods to interact with the Relay API. 21 | """ 22 | 23 | def connect(self) -> bool: 24 | """ 25 | Connects to the configured controller. 26 | 27 | :return: True if successful. 28 | """ 29 | url = self.api_url + '/relay/agent' 30 | response = self.session.put(url) 31 | return response.ok 32 | 33 | 34 | def disconnect(self) -> bool: 35 | """ 36 | Disconnects from the connected controller. 37 | 38 | :return: True if successful. 39 | """ 40 | url = self.api_url + '/relay/agent' 41 | response = self.session.delete(url) 42 | return response.ok 43 | 44 | 45 | def download_file(self, token: str) -> bool: 46 | """ 47 | Downloads a file from the connected controller. 48 | 49 | :return: True if successful. 50 | """ 51 | url = self.api_url + f'/relay/controller/downloads/{token}' 52 | response = self.session.get(url) 53 | return response.ok 54 | 55 | 56 | def upload_file(self, token: str) -> bool: 57 | """ 58 | Uploads a file from the connected controller. 59 | 60 | :return: True if successful. 61 | """ 62 | url = self.api_url + f'/relay/controller/files/{token}' 63 | response = self.session.post(url) 64 | return response.ok 65 | 66 | 67 | def upload_share_info(self, token: str) -> bool: 68 | """ 69 | Uploads share information to the connected controller. 70 | 71 | :return: True if successful. 72 | """ 73 | url = self.api_url + f'/relay/controller/shares/{token}' 74 | response = self.session.post(url) 75 | return response.ok -------------------------------------------------------------------------------- /slskd_api/apis/rooms.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class RoomsApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Rooms API. 21 | """ 22 | 23 | def get_all_joined(self) -> list: 24 | """ 25 | Gets all joined rooms. 26 | 27 | :return: Names of the joined rooms. 28 | """ 29 | url = self.api_url + '/rooms/joined' 30 | response = self.session.get(url) 31 | return response.json() 32 | 33 | 34 | def join(self, roomName: str) -> dict: 35 | """ 36 | Joins a room. 37 | 38 | :return: room info: name, isPrivate, users, messages 39 | """ 40 | url = self.api_url + '/rooms/joined' 41 | response = self.session.post(url, json=roomName) 42 | return response.json() 43 | 44 | 45 | def get_joined(self, roomName: str) -> dict: 46 | """ 47 | Gets the specified room. 48 | 49 | :return: room info: name, isPrivate, users, messages 50 | """ 51 | url = self.api_url + f'/rooms/joined/{quote(roomName)}' 52 | response = self.session.get(url) 53 | return response.json() 54 | 55 | 56 | def leave(self, roomName: str) -> bool: 57 | """ 58 | Leaves a room. 59 | 60 | :return: True if successful. 61 | """ 62 | url = self.api_url + f'/rooms/joined/{quote(roomName)}' 63 | response = self.session.delete(url) 64 | return response.ok 65 | 66 | 67 | def send(self, roomName: str, message: str) -> bool: 68 | """ 69 | Sends a message to the specified room. 70 | 71 | :return: True if successful. 72 | """ 73 | url = self.api_url + f'/rooms/joined/{quote(roomName)}/messages' 74 | response = self.session.post(url, json=message) 75 | return response.ok 76 | 77 | 78 | def get_messages(self, roomName: str) -> list: 79 | """ 80 | Gets the current list of messages for the specified room. 81 | """ 82 | url = self.api_url + f'/rooms/joined/{quote(roomName)}/messages' 83 | response = self.session.get(url) 84 | return response.json() 85 | 86 | 87 | def set_ticker(self, roomName: str, ticker: str) -> bool: 88 | """ 89 | Sets a ticker for the specified room. 90 | 91 | :return: True if successful. 92 | """ 93 | url = self.api_url + f'/rooms/joined/{quote(roomName)}/ticker' 94 | response = self.session.post(url, json=ticker) 95 | return response.ok 96 | 97 | 98 | def add_member(self, roomName: str, username: str) -> bool: 99 | """ 100 | Adds a member to a private room. 101 | 102 | :return: True if successful. 103 | """ 104 | url = self.api_url + f'/rooms/joined/{quote(roomName)}/members' 105 | response = self.session.post(url, json=username) 106 | return response.ok 107 | 108 | 109 | def get_users(self, roomName: str) -> list: 110 | """ 111 | Gets the current list of users for the specified joined room. 112 | """ 113 | url = self.api_url + f'/rooms/joined/{quote(roomName)}/users' 114 | response = self.session.get(url) 115 | return response.json() 116 | 117 | 118 | def get_all(self) -> list: 119 | """ 120 | Gets a list of rooms from the server. 121 | """ 122 | url = self.api_url + '/rooms/available' 123 | response = self.session.get(url) 124 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/searches.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | import uuid 18 | from typing import Optional 19 | 20 | class SearchesApi(BaseApi): 21 | """ 22 | Class that handles operations on searches. 23 | """ 24 | 25 | def search_text(self, 26 | searchText: str, 27 | id: Optional[str] = None, 28 | fileLimit: int = 10000, 29 | filterResponses: bool = True, 30 | maximumPeerQueueLength: int = 1000000, 31 | minimumPeerUploadSpeed: int = 0, 32 | minimumResponseFileCount: int = 1, 33 | responseLimit: int = 100, 34 | searchTimeout: int = 15000 35 | ) -> dict: 36 | """ 37 | Performs a search for the specified request. 38 | 39 | :param searchText: Search query 40 | :param id: uuid of the search. One will be generated if None. 41 | :param fileLimit: Max number of file results 42 | :param filterResponses: Filter unreachable users from the results 43 | :param maximumPeerQueueLength: Max queue length 44 | :param minimumPeerUploadSpeed: Min upload speed in bit/s 45 | :param minimumResponseFileCount: Min number of matching files per user 46 | :param responseLimit: Max number of users results 47 | :param searchTimeout: Search timeout in ms 48 | :return: Info about the search (no results!) 49 | """ 50 | 51 | url = self.api_url + '/searches' 52 | 53 | try: 54 | id = str(uuid.UUID(id)) # check if given id is a valid uuid 55 | except: 56 | id = str(uuid.uuid1()) # otherwise generate a new one 57 | 58 | data = { 59 | "id": id, 60 | "fileLimit": fileLimit, 61 | "filterResponses": filterResponses, 62 | "maximumPeerQueueLength": maximumPeerQueueLength, 63 | "minimumPeerUploadSpeed": minimumPeerUploadSpeed, 64 | "minimumResponseFileCount": minimumResponseFileCount, 65 | "responseLimit": responseLimit, 66 | "searchText": searchText, 67 | "searchTimeout": searchTimeout, 68 | } 69 | response = self.session.post(url, json=data) 70 | return response.json() 71 | 72 | 73 | def get_all(self) -> list: 74 | """ 75 | Gets the list of active and completed searches. 76 | """ 77 | url = self.api_url + '/searches' 78 | response = self.session.get(url) 79 | return response.json() 80 | 81 | 82 | def state(self, id: str, includeResponses: bool = False) -> dict: 83 | """ 84 | Gets the state of the search corresponding to the specified id. 85 | 86 | :param id: uuid of the search. 87 | :param includeResponses: Include responses (search result list) in the returned dict 88 | :return: Info about the search 89 | """ 90 | url = self.api_url + f'/searches/{id}' 91 | params = dict( 92 | includeResponses=includeResponses 93 | ) 94 | response = self.session.get(url, params=params) 95 | return response.json() 96 | 97 | 98 | def stop(self, id: str) -> bool: 99 | """ 100 | Stops the search corresponding to the specified id. 101 | 102 | :return: True if successful. 103 | """ 104 | url = self.api_url + f'/searches/{id}' 105 | response = self.session.put(url) 106 | return response.ok 107 | 108 | 109 | def delete(self, id: str): 110 | """ 111 | Deletes the search corresponding to the specified id. 112 | 113 | :return: True if successful. 114 | """ 115 | url = self.api_url + f'/searches/{id}' 116 | response = self.session.delete(url) 117 | return response.ok 118 | 119 | 120 | def search_responses(self, id: str) -> list: 121 | """ 122 | Gets search responses corresponding to the specified id. 123 | """ 124 | url = self.api_url + f'/searches/{id}/responses' 125 | response = self.session.get(url) 126 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/server.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class ServerApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Server API. 21 | """ 22 | 23 | def connect(self) -> bool: 24 | """ 25 | Connects the client. 26 | 27 | :return: True if successful. 28 | """ 29 | url = self.api_url + '/server' 30 | response = self.session.put(url) 31 | return response.ok 32 | 33 | 34 | def disconnect(self) -> bool: 35 | """ 36 | Disconnects the client. 37 | 38 | :return: True if successful. 39 | """ 40 | url = self.api_url + '/server' 41 | response = self.session.delete(url, json='') 42 | return response.ok 43 | 44 | 45 | def state(self) -> dict: 46 | """ 47 | Retrieves the current state of the server. 48 | """ 49 | url = self.api_url + '/server' 50 | response = self.session.get(url) 51 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/session.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class SessionApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Session API. 21 | """ 22 | 23 | def auth_valid(self) -> bool: 24 | """ 25 | Checks whether the provided authentication is valid. 26 | """ 27 | url = self.api_url + '/session' 28 | response = self.session.get(url) 29 | return response.ok 30 | 31 | 32 | def login(self, username: str, password: str) -> dict: 33 | """ 34 | Logs in. 35 | 36 | :return: Session info for the given user incl. token. 37 | """ 38 | url = self.api_url + '/session' 39 | data = { 40 | 'username': username, 41 | 'password': password 42 | } 43 | response = self.session.post(url, json=data) 44 | return response.json() 45 | 46 | 47 | def security_enabled(self) -> bool: 48 | """ 49 | Checks whether security is enabled. 50 | """ 51 | url = self.api_url + '/session/enabled' 52 | response = self.session.get(url) 53 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/shares.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class SharesApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Shares API. 21 | """ 22 | 23 | def get_all(self) -> dict: 24 | """ 25 | Gets the current list of shares. 26 | """ 27 | url = self.api_url + '/shares' 28 | response = self.session.get(url) 29 | return response.json() 30 | 31 | 32 | def start_scan(self) -> bool: 33 | """ 34 | Initiates a scan of the configured shares. 35 | 36 | :return: True if successful. 37 | """ 38 | url = self.api_url + '/shares' 39 | response = self.session.put(url) 40 | return response.ok 41 | 42 | 43 | def cancel_scan(self) -> bool: 44 | """ 45 | Cancels a share scan, if one is running. 46 | 47 | :return: True if successful. 48 | """ 49 | url = self.api_url + '/shares' 50 | response = self.session.delete(url) 51 | return response.ok 52 | 53 | 54 | def get(self, id: str) -> dict: 55 | """ 56 | Gets the share associated with the specified id. 57 | """ 58 | url = self.api_url + f'/shares/{id}' 59 | response = self.session.get(url) 60 | return response.json() 61 | 62 | 63 | def all_contents(self) -> list: 64 | """ 65 | Returns a list of all shared directories and files. 66 | """ 67 | url = self.api_url + '/shares/contents' 68 | response = self.session.get(url) 69 | return response.json() 70 | 71 | 72 | def contents(self, id: str) -> list: 73 | """ 74 | Gets the contents of the share associated with the specified id. 75 | """ 76 | url = self.api_url + f'/shares/{id}/contents' 77 | response = self.session.get(url) 78 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/transfers.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | from typing import Union 18 | 19 | class TransfersApi(BaseApi): 20 | """ 21 | This class contains the methods to interact with the Transfers API. 22 | """ 23 | 24 | def cancel_download(self, username: str, id:str, remove: bool = False) -> bool: 25 | """ 26 | Cancels the specified download. 27 | 28 | :return: True if successful. 29 | """ 30 | url = self.api_url + f'/transfers/downloads/{quote(username)}/{id}' 31 | params = dict( 32 | remove=remove 33 | ) 34 | response = self.session.delete(url, params=params) 35 | return response.ok 36 | 37 | 38 | def get_download(self, username: str, id: str) -> dict: 39 | """ 40 | Gets the specified download. 41 | """ 42 | url = self.api_url + f'/transfers/downloads/{quote(username)}/{id}' 43 | response = self.session.get(url) 44 | return response.json() 45 | 46 | 47 | def remove_completed_downloads(self) -> bool: 48 | """ 49 | Removes all completed downloads, regardless of whether they failed or succeeded. 50 | 51 | :return: True if successful. 52 | """ 53 | url = self.api_url + '/transfers/downloads/all/completed' 54 | response = self.session.delete(url) 55 | return response.ok 56 | 57 | 58 | def cancel_upload(self, username: str, id: str, remove: bool = False) -> bool: 59 | """ 60 | Cancels the specified upload. 61 | 62 | :return: True if successful. 63 | """ 64 | url = self.api_url + f'/transfers/uploads/{quote(username)}/{id}' 65 | params = dict( 66 | remove=remove 67 | ) 68 | response = self.session.delete(url, params=params) 69 | return response.ok 70 | 71 | 72 | def get_upload(self, username: str, id: str) -> dict: 73 | """ 74 | Gets the specified upload. 75 | """ 76 | url = self.api_url + f'/transfers/uploads/{quote(username)}/{id}' 77 | response = self.session.get(url) 78 | return response.json() 79 | 80 | 81 | def remove_completed_uploads(self) -> bool: 82 | """ 83 | Removes all completed uploads, regardless of whether they failed or succeeded. 84 | 85 | :return: True if successful. 86 | """ 87 | url = self.api_url + '/transfers/uploads/all/completed' 88 | response = self.session.delete(url) 89 | return response.ok 90 | 91 | 92 | def enqueue(self, username: str, files: list) -> bool: 93 | """ 94 | Enqueues the specified download. 95 | 96 | :param username: User to download from. 97 | :param files: A list of dictionaries in the same form as what's returned 98 | by :py:func:`~slskd_api.apis.SearchesApi.search_responses`: 99 | [{'filename': , 'size': }...] 100 | :return: True if successful. 101 | """ 102 | url = self.api_url + f'/transfers/downloads/{quote(username)}' 103 | response = self.session.post(url, json=files) 104 | return response.ok 105 | 106 | 107 | def get_downloads(self, username: str) -> dict: 108 | """ 109 | Gets all downloads for the specified username. 110 | """ 111 | url = self.api_url + f'/transfers/downloads/{quote(username)}' 112 | response = self.session.get(url) 113 | return response.json() 114 | 115 | 116 | def get_all_downloads(self, includeRemoved: bool = False) -> list: 117 | """ 118 | Gets all downloads. 119 | """ 120 | url = self.api_url + '/transfers/downloads/' 121 | params = dict( 122 | includeRemoved=includeRemoved 123 | ) 124 | response = self.session.get(url, params=params) 125 | return response.json() 126 | 127 | 128 | def get_queue_position(self, username: str, id: str) -> Union[int,str]: 129 | """ 130 | Gets the download for the specified username matching the specified filename, and requests the current place in the remote queue of the specified download. 131 | 132 | :return: Queue position or error message 133 | """ 134 | url = self.api_url + f'/transfers/downloads/{quote(username)}/{id}/position' 135 | response = self.session.get(url) 136 | return response.json() 137 | 138 | 139 | def get_all_uploads(self, includeRemoved: bool = False) -> list: 140 | """ 141 | Gets all uploads. 142 | """ 143 | url = self.api_url + '/transfers/uploads/' 144 | params = dict( 145 | includeRemoved=includeRemoved 146 | ) 147 | response = self.session.get(url, params=params) 148 | return response.json() 149 | 150 | 151 | def get_uploads(self, username: str) -> dict: 152 | """ 153 | Gets all uploads for the specified username. 154 | """ 155 | url = self.api_url + f'/transfers/uploads/{quote(username)}' 156 | response = self.session.get(url) 157 | return response.json() -------------------------------------------------------------------------------- /slskd_api/apis/users.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from .base import * 17 | 18 | class UsersApi(BaseApi): 19 | """ 20 | This class contains the methods to interact with the Users API. 21 | """ 22 | 23 | def address(self, username: str) -> dict: 24 | """ 25 | Retrieves the address of the specified username. 26 | """ 27 | url = self.api_url + f'/users/{quote(username)}/endpoint' 28 | response = self.session.get(url) 29 | return response.json() 30 | 31 | 32 | def browse(self, username: str) -> dict: 33 | """ 34 | Retrieves the files shared by the specified username. 35 | """ 36 | url = self.api_url + f'/users/{quote(username)}/browse' 37 | response = self.session.get(url) 38 | return response.json() 39 | 40 | 41 | def browsing_status(self, username: str) -> dict: 42 | """ 43 | Retrieves the status of the current browse operation for the specified username, if any. 44 | Will return error 404 if called after the browsing operation has ended. 45 | Best called asynchronously while :py:func:`browse` is still running. 46 | """ 47 | url = self.api_url + f'/users/{quote(username)}/browse/status' 48 | response = self.session.get(url) 49 | return response.json() 50 | 51 | 52 | def directory(self, username: str, directory: str) -> dict: 53 | """ 54 | Retrieves the files from the specified directory from the specified username. 55 | """ 56 | url = self.api_url + f'/users/{quote(username)}/directory' 57 | data = { 58 | "directory": directory 59 | } 60 | response = self.session.post(url, json=data) 61 | return response.json() 62 | 63 | 64 | def info(self, username: str) -> dict: 65 | """ 66 | Retrieves information about the specified username. 67 | """ 68 | url = self.api_url + f'/users/{quote(username)}/info' 69 | response = self.session.get(url) 70 | return response.json() 71 | 72 | 73 | def status(self, username: str) -> dict: 74 | """ 75 | Retrieves status for the specified username. 76 | """ 77 | url = self.api_url + f'/users/{quote(username)}/status' 78 | response = self.session.get(url) 79 | return response.json() -------------------------------------------------------------------------------- /slskd_api/client.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 bigoulours 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | API_VERSION = 'v0' 17 | 18 | import requests 19 | from urllib.parse import urljoin 20 | from functools import reduce 21 | from base64 import b64encode 22 | from slskd_api.apis import * 23 | 24 | 25 | class HTTPAdapterTimeout(requests.adapters.HTTPAdapter): 26 | def __init__(self, timeout=None, **kwargs): 27 | super().__init__(**kwargs) 28 | self.timeout = timeout 29 | 30 | def send(self, *args, **kwargs): 31 | kwargs['timeout'] = self.timeout 32 | return super().send(*args, **kwargs) 33 | 34 | 35 | class SlskdClient: 36 | """ 37 | The main class that allows access to the different APIs of a slskd instance. 38 | An API-Key with appropriate permissions (`readwrite` for most use cases) must be set in slskd config file. 39 | Alternatively, provide your username and password. Requests error status raise corresponding error. 40 | Usage:: 41 | slskd = slskd_api.SlskdClient(host, api_key, url_base) 42 | app_status = slskd.application.state() 43 | """ 44 | 45 | def __init__(self, 46 | host: str, 47 | api_key: str = None, 48 | url_base: str = '/', 49 | username: str = None, 50 | password: str = None, 51 | token: str = None, 52 | verify_ssl: bool = True, 53 | timeout: float = None # requests timeout in seconds 54 | ): 55 | api_url = reduce(urljoin, [f'{host}/', f'{url_base}/', f'api/{API_VERSION}']) 56 | 57 | session = requests.Session() 58 | session.adapters['http://'] = HTTPAdapterTimeout(timeout=timeout) 59 | session.adapters['https://'] = HTTPAdapterTimeout(timeout=timeout) 60 | session.hooks = {'response': lambda r, *args, **kwargs: r.raise_for_status()} 61 | session.headers.update({'accept': '*/*'}) 62 | session.verify = verify_ssl 63 | 64 | header = {} 65 | 66 | if api_key: 67 | header['X-API-Key'] = api_key 68 | elif username and password: 69 | header['Authorization'] = 'Bearer ' + \ 70 | SessionApi(api_url, session).login(username, password).get('token', '') 71 | elif token: 72 | header['Authorization'] = 'Bearer ' + token 73 | else: 74 | raise ValueError('Please provide an API-Key, a valid token or username/password.') 75 | 76 | session.headers.update(header) 77 | 78 | base_args = (api_url, session) 79 | 80 | self.application = ApplicationApi(*base_args) 81 | self.conversations = ConversationsApi(*base_args) 82 | self.logs = LogsApi(*base_args) 83 | self.options = OptionsApi(*base_args) 84 | self.public_chat = PublicChatApi(*base_args) 85 | self.relay = RelayApi(*base_args) 86 | self.rooms = RoomsApi(*base_args) 87 | self.searches = SearchesApi(*base_args) 88 | self.server = ServerApi(*base_args) 89 | self.session = SessionApi(*base_args) 90 | self.shares = SharesApi(*base_args) 91 | self.transfers = TransfersApi(*base_args) 92 | self.users = UsersApi(*base_args) 93 | 94 | 95 | class MetricsApi: 96 | """ 97 | Getting the metrics works with a different endpoint. Default: :5030/metrics. 98 | Metrics should be first activated in slskd config file. 99 | User/pass is independent from the main application and default value (slskd:slskd) should be changed. 100 | Usage:: 101 | metrics_api = slskd_api.MetricsApi(host, metrics_usr='slskd', metrics_pwd='slskd') 102 | metrics = metrics_api.get() 103 | """ 104 | 105 | def __init__(self, 106 | host: str, 107 | metrics_usr: str = 'slskd', 108 | metrics_pwd: str = 'slskd', 109 | metrics_url_base: str = '/metrics' 110 | ): 111 | self.metrics_url = urljoin(host, metrics_url_base) 112 | basic_auth = b64encode(bytes(f'{metrics_usr}:{metrics_pwd}', 'utf-8')) 113 | self.header = { 114 | 'accept': '*/*', 115 | 'Authorization': f'Basic {basic_auth.decode()}' 116 | } 117 | 118 | def get(self) -> str: 119 | """ 120 | Gets the Prometheus metrics as text. 121 | """ 122 | response = requests.get(self.metrics_url, headers=self.header) 123 | return response.text 124 | --------------------------------------------------------------------------------