Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
|
@ -1,72 +0,0 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '15 10 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
|
@ -1 +0,0 @@
|
|||
.env
|
661
LICENSE
661
LICENSE
|
@ -1,661 +0,0 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1 @@
|
|||
theme: jekyll-theme-midnight
|
Binary file not shown.
Before Width: | Height: | Size: 8.3 KiB |
|
@ -1 +0,0 @@
|
|||
["\u043b\u043e\u0445", "\u043b\u043e\u0448\u043f\u0435\u0434", "\u043b\u043e\u0445\u043e\u0442\u0440\u043e\u043d", "\u0431\u043b\u044f\u0442\u044c", "\u043f\u0437\u0434\u0446", "discord.gg/", "discord.com/invite/", "https://discord.com/invite/"]
|
15
cenz.txt
15
cenz.txt
|
@ -1,15 +0,0 @@
|
|||
лох
|
||||
|
||||
лошпед
|
||||
|
||||
лохотрон
|
||||
|
||||
блять
|
||||
|
||||
пздц
|
||||
|
||||
discord.gg/
|
||||
|
||||
discord.com/invite/
|
||||
|
||||
https://discord.com/invite/
|
|
@ -1,39 +0,0 @@
|
|||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
import random
|
||||
import asyncio
|
||||
|
||||
|
||||
class guessing_game(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx, error):
|
||||
if isinstance(error, commands.CommandOnCooldown):
|
||||
await ctx.send(f'Для использования команды повторно жди {round(error.retry_after, 2)} секунд')
|
||||
|
||||
|
||||
@commands.command(aliases = ['guess', 'номера', 'угадайка'])
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
async def guessing_game(self, ctx):
|
||||
await ctx.send('Введи число от 1 до 10.')
|
||||
|
||||
def is_correct(m):
|
||||
return m.author == ctx.author and m.content.isdigit()
|
||||
|
||||
answer = random.randint(1, 10)
|
||||
|
||||
try:
|
||||
guess = await self.bot.wait_for('message', check=is_correct, timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
return await ctx.send(f'Извини, тебе потребовалось слишком много времени, чтобы это было {answer}.')
|
||||
|
||||
if int(guess.content) == answer:
|
||||
await ctx.send('Ты угадал!')
|
||||
else:
|
||||
await ctx.send(f'Ты не угадал. Я загадывал {answer}.')
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(guessing_game(bot))
|
|
@ -1,27 +0,0 @@
|
|||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
import random
|
||||
|
||||
class head_or_tails(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx, error):
|
||||
if isinstance(error, commands.CommandOnCooldown):
|
||||
await ctx.send(f'Для использования команды повторно жди {round(error.retry_after, 2)} секунд')
|
||||
|
||||
@commands.command(aliases = ["орёл_или_решка", "монетка", "монета"])
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
async def headortails(self, ctx, answer=None):
|
||||
if answer == None:
|
||||
await ctx.reply("Можно использовать только значения Орёл и Решка. Пример команды: $монета Орёл")
|
||||
else:
|
||||
if random.choice(["Орёл", "Решка"]) == answer:
|
||||
await ctx.reply("Ты выиграл!")
|
||||
else:
|
||||
await ctx.reply("Ты проиграл :(")
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(head_or_tails(bot))
|
|
@ -1,57 +0,0 @@
|
|||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
import datetime
|
||||
|
||||
now = datetime.datetime.now()
|
||||
mhour = now.hour + 3
|
||||
msctime = now.strftime(f"%d-%m-%Y {mhour}:%M")
|
||||
|
||||
|
||||
class Logs(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
msg = f"{member.name} зашёл на сервер."
|
||||
await self.bot.get_channel(channelid).send(msg)
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member):
|
||||
msg = f"{member.name} вышел с сервера."
|
||||
await self.bot.get_channel(channelid).send(msg)
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message_edit(self, before, after, member: nextcord.Member):
|
||||
msg = f"{msctime}\n" \
|
||||
f"Сообщение до изменений {before.content}\n" \
|
||||
f"Сообщение после изменений {after.content}"
|
||||
await self.bot.get_channel(channelid).send(msg)
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message_delete(self, message, member: nextcord.Member):
|
||||
msg = f"{msctime}\n" \
|
||||
f"Удалённое сообщение: {message.content}\n"
|
||||
await self.bot.get_channel(channelid).send(msg)
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_voice_state_update(self, member: nextcord.Member, before: nextcord.VoiceState, after: nextcord.VoiceState):
|
||||
if before.channel is None:
|
||||
msg = f"{msctime}\n" \
|
||||
f"{member.display_name} зашел в канал {after.channel.mention}"
|
||||
elif after.channel is None:
|
||||
msg = f"{msctime}\n" \
|
||||
f"{member.display_name} покинул канап {before.channel.mention}"
|
||||
elif before.channel != after.channel:
|
||||
msg = f"{msctime}\n" \
|
||||
f"{member.display_name} перешел из канала {before. channel.mention} в канал {after.channel.mention}"
|
||||
await self.bot.get_channel (channelid).send(msg)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Logs(bot))
|
866
cogs/music.py
866
cogs/music.py
|
@ -1,866 +0,0 @@
|
|||
import asyncio
|
||||
import functools
|
||||
import itertools
|
||||
import math
|
||||
import random
|
||||
import os
|
||||
import time
|
||||
|
||||
import nextcord
|
||||
import youtube_dl
|
||||
from async_timeout import timeout
|
||||
from nextcord.ext import commands
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
|
||||
# Silence useless bug reports messages
|
||||
youtube_dl.utils.bug_reports_message = lambda: ''
|
||||
|
||||
|
||||
class VoiceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class YTDLError(Exception):
|
||||
pass
|
||||
|
||||
class FFMPEGSource(nextcord.PCMVolumeTransformer):
|
||||
def __init__(self, ctx: commands.Context, source: nextcord.FFmpegPCMAudio, *, data: dict, volume: float = 0.5):
|
||||
super().__init__(source, volume)
|
||||
|
||||
self.requester = ctx.author
|
||||
self.channel = ctx.channel
|
||||
self.data = data
|
||||
self.title = data.get('title')
|
||||
self.url = data.get('url')
|
||||
self.duration = self.parse_duration(int(data.get('duration')))
|
||||
self.duration_raw = self.parse_duration_raw(int(data.get('duration')))
|
||||
self.duration_int = int(data.get('duration'))
|
||||
|
||||
def __str__(self):
|
||||
return '**{0.title}** by **{0.uploader}**'.format(self)
|
||||
|
||||
@staticmethod
|
||||
def parse_duration(duration: int):
|
||||
minutes, seconds = divmod(duration, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
|
||||
duration = []
|
||||
if days > 0:
|
||||
duration.append('{} дней'.format(days))
|
||||
if hours > 0:
|
||||
duration.append('{} часов'.format(hours))
|
||||
if minutes > 0:
|
||||
duration.append('{} минут'.format(minutes))
|
||||
if seconds > 0:
|
||||
duration.append('{} секунд'.format(seconds))
|
||||
|
||||
return ', '.join(duration)
|
||||
|
||||
@staticmethod
|
||||
def parse_duration_raw(duration: int):
|
||||
minutes, seconds = divmod(duration, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
|
||||
durations = []
|
||||
if days > 0:
|
||||
durations.append(str(days))
|
||||
if hours > 0:
|
||||
durations.append(("0" if days and hours < 10 else "") + '{}'.format(hours))
|
||||
durations.append(("0" if hours and minutes < 10 else "") + '{}'.format(minutes))
|
||||
durations.append(("0" if seconds < 10 else "") + '{}'.format(seconds))
|
||||
|
||||
return ':'.join(durations)
|
||||
|
||||
class YTDLSource(nextcord.PCMVolumeTransformer):
|
||||
YTDL_OPTIONS_PLAYLIST = {
|
||||
'format': 'bestaudio/best',
|
||||
'extractaudio': True,
|
||||
'audioformat': 'mp3',
|
||||
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
|
||||
'restrictfilenames': True,
|
||||
'nocheckcertificate': True,
|
||||
'ignoreerrors': False,
|
||||
'logtostderr': False,
|
||||
'quiet': True,
|
||||
'no_warnings': True,
|
||||
'default_search': 'auto',
|
||||
'source_address': '0.0.0.0',
|
||||
'extract_flat': 'in_playlist',
|
||||
}
|
||||
YTDL_OPTIONS = {
|
||||
'format': 'bestaudio/best',
|
||||
'extractaudio': True,
|
||||
'audioformat': 'mp3',
|
||||
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
|
||||
'restrictfilenames': True,
|
||||
'noplaylist': True,
|
||||
'nocheckcertificate': True,
|
||||
'ignoreerrors': False,
|
||||
'logtostderr': False,
|
||||
'quiet': True,
|
||||
'no_warnings': True,
|
||||
'default_search': 'auto',
|
||||
'source_address': '0.0.0.0',
|
||||
}
|
||||
FFMPEG_OPTIONS = {
|
||||
'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
|
||||
'options': '-vn',
|
||||
}
|
||||
|
||||
ytdl = youtube_dl.YoutubeDL(YTDL_OPTIONS)
|
||||
ytdl_playlist = youtube_dl.YoutubeDL(YTDL_OPTIONS_PLAYLIST)
|
||||
|
||||
def __init__(self, ctx: commands.Context, source: nextcord.FFmpegPCMAudio, *, data: dict, volume: float = 0.5):
|
||||
super().__init__(source, volume)
|
||||
|
||||
self.requester = data.get('requester')
|
||||
self.channel = ctx.channel
|
||||
self.data = data
|
||||
|
||||
self.uploader = data.get('uploader')
|
||||
self.uploader_url = data.get('uploader_url')
|
||||
date = data.get('upload_date')
|
||||
self.upload_date = date[6:8] + '.' + date[4:6] + '.' + date[0:4]
|
||||
self.title = data.get('title')
|
||||
self.thumbnail = data.get('thumbnail')
|
||||
self.description = data.get('description')
|
||||
self.duration = self.parse_duration(int(data.get('duration')))
|
||||
self.duration_raw = self.parse_duration_raw(int(data.get('duration')))
|
||||
self.duration_int = int(data.get('duration'))
|
||||
self.tags = data.get('tags')
|
||||
self.url = data.get('webpage_url')
|
||||
self.views = data.get('view_count')
|
||||
self.likes = data.get('like_count')
|
||||
self.dislikes = data.get('dislike_count')
|
||||
self.stream_url = data.get('url')
|
||||
|
||||
def __str__(self):
|
||||
return '**{0.title}** by **{0.uploader}**'.format(self)
|
||||
|
||||
@classmethod
|
||||
async def create_source(self, ctx: commands.Context, search: str, *, loop: asyncio.BaseEventLoop = None, requester=None):
|
||||
loop = loop or asyncio.get_event_loop()
|
||||
|
||||
partial = functools.partial(self.ytdl.extract_info, search, download=False, process=False)
|
||||
data = await loop.run_in_executor(None, partial)
|
||||
|
||||
if data is None:
|
||||
raise YTDLError('Couldn\'t find anything that matches `{}`'.format(search))
|
||||
|
||||
if 'entries' not in data:
|
||||
process_info = data
|
||||
else:
|
||||
process_info = None
|
||||
for entry in data['entries']:
|
||||
if entry:
|
||||
process_info = entry
|
||||
break
|
||||
|
||||
if process_info is None:
|
||||
raise YTDLError('Couldn\'t find anything that matches `{}`'.format(search))
|
||||
|
||||
webpage_url = process_info['webpage_url']
|
||||
partial = functools.partial(self.ytdl.extract_info, webpage_url, download=False)
|
||||
processed_info = await loop.run_in_executor(None, partial)
|
||||
|
||||
if processed_info is None:
|
||||
raise YTDLError('Couldn\'t fetch `{}`'.format(webpage_url))
|
||||
|
||||
if 'entries' not in processed_info:
|
||||
info = processed_info
|
||||
else:
|
||||
info = None
|
||||
while info is None:
|
||||
try:
|
||||
info = processed_info['entries'].pop(0)
|
||||
except IndexError:
|
||||
raise YTDLError('Couldn\'t retrieve any matches for `{}`'.format(webpage_url))
|
||||
info["requester"] = requester
|
||||
return self(ctx, nextcord.FFmpegPCMAudio(info['url'], **self.FFMPEG_OPTIONS), data=info)
|
||||
|
||||
@staticmethod
|
||||
def parse_duration(duration: int):
|
||||
minutes, seconds = divmod(duration, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
|
||||
duration = []
|
||||
if days > 0:
|
||||
duration.append('{} дней'.format(days))
|
||||
if hours > 0:
|
||||
duration.append('{} часов'.format(hours))
|
||||
if minutes > 0:
|
||||
duration.append('{} минут'.format(minutes))
|
||||
if seconds > 0:
|
||||
duration.append('{} секунд'.format(seconds))
|
||||
|
||||
return ', '.join(duration)
|
||||
|
||||
@staticmethod
|
||||
def parse_duration_raw(duration: int):
|
||||
minutes, seconds = divmod(duration, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
days, hours = divmod(hours, 24)
|
||||
|
||||
durations = []
|
||||
if days > 0:
|
||||
durations.append(str(days))
|
||||
if hours > 0:
|
||||
durations.append(("0" if days and hours < 10 else "") + '{}'.format(hours))
|
||||
durations.append(("0" if hours and minutes < 10 else "") + '{}'.format(minutes))
|
||||
durations.append(("0" if seconds < 10 else "") + '{}'.format(seconds))
|
||||
|
||||
return ':'.join(durations)
|
||||
|
||||
class Song:
|
||||
__slots__ = ('source', 'requester', 'starttime', 'pause_time', 'pause_duration', 'paused', 'isFile')
|
||||
|
||||
def __init__(self, source, isFile=False):
|
||||
self.source = source
|
||||
self.requester = source.requester
|
||||
self.starttime = None
|
||||
self.pause_duration = 0
|
||||
self.pause_time = 0
|
||||
self.paused = False
|
||||
self.isFile = isFile
|
||||
|
||||
def create_embed(self, status: str):
|
||||
# If a new song is being played, it will simply display how long the song is
|
||||
# But if the command now is being executed, it will show how long the song has been played
|
||||
if self.paused:
|
||||
self.pause_duration += time.time() - self.pause_time
|
||||
self.pause_time = time.time()
|
||||
embed = (nextcord.Embed(title='Воспроизводится',
|
||||
description='```css\n{}\n```'.format(self.source.title),
|
||||
color=nextcord.Color.blurple())
|
||||
.add_field(name='Длительность', value=(self.source.duration if status == "play" else YTDLSource.parse_duration_raw(int(time.time() - self.starttime - self.pause_duration)) + "/" + self.source.duration_raw))
|
||||
.add_field(name='Запросил', value=self.requester.mention))
|
||||
# If it is not a file, it is a youtube video
|
||||
if not self.isFile:
|
||||
embed.add_field(name='Выложил', value='[{0.source.uploader}]({0.source.uploader_url})'.format(self))
|
||||
embed.add_field(name='URL', value='[Жмяк!]({0.source.url})'.format(self))
|
||||
embed.set_thumbnail(url=self.source.thumbnail)
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
class SongQueue(asyncio.Queue):
|
||||
def __getitem__(self, item):
|
||||
if isinstance(item, slice):
|
||||
return list(itertools.islice(self._queue, item.start, item.stop, item.step))
|
||||
else:
|
||||
return self._queue[item]
|
||||
|
||||
def __iter__(self):
|
||||
return self._queue.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
return self.qsize()
|
||||
|
||||
def clear(self):
|
||||
self._queue.clear()
|
||||
|
||||
def shuffle(self):
|
||||
random.shuffle(self._queue)
|
||||
|
||||
def remove(self, index: int):
|
||||
del self._queue[index]
|
||||
|
||||
|
||||
class VoiceState:
|
||||
def __init__(self, bot: commands.Bot, ctx: commands.Context):
|
||||
self.bot = bot
|
||||
self._ctx = ctx
|
||||
|
||||
self.current = None
|
||||
self.voice = None
|
||||
self.next = asyncio.Event()
|
||||
self.songs = SongQueue()
|
||||
|
||||
self._loop = False
|
||||
self._volume = 0.5
|
||||
self.skip_votes = set()
|
||||
|
||||
self.audio_player = bot.loop.create_task(self.audio_player_task())
|
||||
self.skipped = False
|
||||
self.pause_time = 0.0
|
||||
self.pause_duration = 0.0
|
||||
|
||||
self.loopqueue = False
|
||||
|
||||
def recreate_bg_task(self, ctx):
|
||||
self.__init__(self.bot, ctx)
|
||||
|
||||
def ffmpegsource(self, path):
|
||||
return nextcord.FFmpegPCMAudio(path)
|
||||
|
||||
def __del__(self):
|
||||
self.audio_player.cancel()
|
||||
|
||||
@property
|
||||
def loop(self):
|
||||
return self._loop
|
||||
|
||||
@loop.setter
|
||||
def loop(self, value: bool):
|
||||
self._loop = value
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self._volume
|
||||
|
||||
@volume.setter
|
||||
def volume(self, value: float):
|
||||
self._volume = value
|
||||
|
||||
@property
|
||||
def is_playing(self):
|
||||
return self.voice and self.current
|
||||
|
||||
async def update_volume(self):
|
||||
# If it is not playing, dont check
|
||||
while self.is_playing:
|
||||
# Without sleep, it will cause lag (at least it lagged on my laptop hahahahahah)
|
||||
await asyncio.sleep(1)
|
||||
# If the volume is updated, update it
|
||||
if not isinstance(self.current, dict) and self.current and self.current.source.volume != self._volume:
|
||||
self.current.source.volume = self._volume
|
||||
|
||||
async def create_song_source(self, ctx, url, title=None, requester=None):
|
||||
if "local@" in url:
|
||||
# It is a local file
|
||||
url = url[6:]
|
||||
try:
|
||||
duration = str(int(float(subprocess.check_output("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"{}\"".format(url), shell=True).decode("ascii").replace("\r", "").replace("\n", ""))))
|
||||
except:
|
||||
return "error"
|
||||
return Song(FFMPEGSource(ctx, self.ffmpegsource(url), data={'duration': duration, 'title': title, 'url': "local@" + url, 'requester': requester}), True)
|
||||
else:
|
||||
return Song(await YTDLSource.create_source(ctx, url, loop=self.bot.loop, requester=requester))
|
||||
|
||||
async def audio_player_task(self):
|
||||
while True:
|
||||
self.next.clear()
|
||||
|
||||
if not self.loop:
|
||||
# Try to get the next song within 3 minutes.
|
||||
# If no song will be added to the queue in time,
|
||||
# the player will disconnect due to performance
|
||||
# reasons.
|
||||
try:
|
||||
async with timeout(180): # 3 minutes
|
||||
# If it is skipped, clear the current song
|
||||
if self.skipped:
|
||||
self.current = None
|
||||
self.current = await self.songs.get()
|
||||
# If the url contains local@, it is a local file
|
||||
if "local@" in self.current["url"]:
|
||||
self.current = await self.create_song_source(self._ctx, self.current["url"], title=self.current["title"], requester=self.current["user"])
|
||||
else:
|
||||
self.current = await self.create_song_source(self._ctx, self.current["url"], requester=self.current["user"])
|
||||
if self.current != "error":
|
||||
# If loop queue, put the current song back to the end of the queue
|
||||
if self.loopqueue:
|
||||
await self.songs.put({"url": self.current.source.url, "title": self.current.source.title, "user": self.current.source.requester})
|
||||
self.skipped = False
|
||||
self.stopped = False
|
||||
except asyncio.TimeoutError:
|
||||
await self.stop(leave=True)
|
||||
return
|
||||
else:
|
||||
# Loop but skipped, proceed to next song and keep looping
|
||||
if self.skipped or self.stopped:
|
||||
self.current = None
|
||||
try:
|
||||
async with timeout(180): # 3 minutes
|
||||
self.current = await self.songs.get()
|
||||
if "local@" in self.current["url"]:
|
||||
self.current = await self.create_song_source(self._ctx, self.current["url"], title=self.current["title"], requester=self.current["user"])
|
||||
else:
|
||||
self.current = await self.create_song_source(self._ctx, self.current["url"], requester=self.current["user"])
|
||||
if self.current != "error":
|
||||
self.skipped = False
|
||||
self.stopped = False
|
||||
except asyncio.TimeoutError:
|
||||
await self.stop(leave=True)
|
||||
return
|
||||
else:
|
||||
if "local@" in self.current.source.url:
|
||||
self.current = await self.create_song_source(self._ctx, self.current.source.url, title=self.current.source.title, requester=self.current.source.requester)
|
||||
else:
|
||||
self.current = await self.create_song_source(self._ctx, self.current.source.url, requester=self.current.source.requester)
|
||||
if self.current != "error":
|
||||
self.current.source.volume = self._volume
|
||||
self.voice.play(self.current.source, after=self.play_next_song)
|
||||
self.current.starttime = time.time()
|
||||
message = await self.current.source.channel.send(embed=self.current.create_embed("play"))
|
||||
# Create task for updating volume
|
||||
self.bot.loop.create_task(self.update_volume())
|
||||
await self.next.wait()
|
||||
await message.delete()
|
||||
|
||||
def play_next_song(self, error=None):
|
||||
if error:
|
||||
raise VoiceError(str(error))
|
||||
|
||||
if not self.loop:
|
||||
self.current = None
|
||||
self.next.set()
|
||||
|
||||
def skip(self):
|
||||
self.skip_votes.clear()
|
||||
self.skipped = True
|
||||
if self.is_playing:
|
||||
self.voice.stop()
|
||||
|
||||
async def stop(self, leave=False):
|
||||
self.songs.clear()
|
||||
self.current = None
|
||||
if self.voice:
|
||||
self.voice.stop()
|
||||
if leave:
|
||||
await self.voice.disconnect()
|
||||
self.voice = None
|
||||
|
||||
|
||||
class Music(commands.Cog):
|
||||
async def respond(self, ctx: commands.Context, message: str=None, embed: nextcord.Embed=None, reply: bool=False):
|
||||
if reply:
|
||||
return await ctx.message.reply(message, embed=embed)
|
||||
else:
|
||||
return await ctx.send(message, embed=embed)
|
||||
|
||||
async def retrieveSong(self, ctx, url, playlist, pos):
|
||||
loop = asyncio.get_event_loop()
|
||||
partial = functools.partial(YTDLSource.ytdl.extract_info, url, download=False)
|
||||
processed_info = await loop.run_in_executor(None, partial)
|
||||
if processed_info is None:
|
||||
raise YTDLError('Couldn\'t fetch `{}`'.format(url))
|
||||
|
||||
if 'entries' not in processed_info:
|
||||
info = processed_info
|
||||
else:
|
||||
info = None
|
||||
while info is None:
|
||||
try:
|
||||
info = processed_info['entries'].pop(0)
|
||||
except IndexError:
|
||||
raise YTDLError('Couldn\'t retrieve any matches for `{}`'.format(url))
|
||||
playlist.append([pos, Song(YTDLSource(ctx, nextcord.FFmpegPCMAudio(info['url'], **YTDLSource.FFMPEG_OPTIONS), data=info))])
|
||||
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
self.voice_states = {}
|
||||
|
||||
def get_voice_state(self, ctx: commands.Context):
|
||||
state = self.voice_states.get(ctx.guild.id)
|
||||
if not state:
|
||||
state = VoiceState(self.bot, ctx)
|
||||
self.voice_states[ctx.guild.id] = state
|
||||
if state.audio_player.done():
|
||||
state.recreate_bg_task(ctx)
|
||||
return state
|
||||
|
||||
def cog_unload(self):
|
||||
for state in self.voice_states.values():
|
||||
self.bot.loop.create_task(state.stop(leave=True))
|
||||
for voicestate in self.voice_state:
|
||||
del self.voice_states[voicestate]
|
||||
shutil.rmtree("./tempMusic")
|
||||
|
||||
def cog_check(self, ctx: commands.Context):
|
||||
if not ctx.guild:
|
||||
raise commands.NoPrivateMessage('This command can\'t be used in DM channels.')
|
||||
|
||||
return True
|
||||
|
||||
async def cog_before_invoke(self, ctx: commands.Context):
|
||||
ctx.voice_state = self.get_voice_state(ctx)
|
||||
|
||||
#async def cog_command_error(self, ctx: commands.Context, error: commands.CommandError):
|
||||
# await ctx.send('An error occurred: {}'.format(str(error)))
|
||||
|
||||
|
||||
@commands.command(name='join', invoke_without_subcommand=True)
|
||||
async def _join(self, ctx: commands.Context):
|
||||
"""Joins a voice channel."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel:
|
||||
await self.respond(ctx, "You are not connected to any voice channel.")
|
||||
return False
|
||||
|
||||
if ctx.voice_client:
|
||||
if ctx.voice_client.channel != ctx.author.voice.channel:
|
||||
await self.respond(ctx, "Bot is already in a voice channel.")
|
||||
return False
|
||||
|
||||
destination = ctx.author.voice.channel
|
||||
|
||||
# Check permission
|
||||
if not destination.permissions_for(ctx.me).connect:
|
||||
return await self.respond(ctx, "No permission to join the voice channel!")
|
||||
|
||||
ctx.voice_state.voice = await destination.connect()
|
||||
await self.respond(ctx, "Зашёл в **{}**.".format(destination))
|
||||
|
||||
if isinstance(ctx.author.voice.channel, nextcord.StageChannel):
|
||||
try:
|
||||
await asyncio.sleep(1)
|
||||
await ctx.me.edit(suppress=False)
|
||||
except:
|
||||
await self.respond(ctx, "I have no permission to speak! Please invite me to speak")
|
||||
if os.path.isdir("./tempMusic/" + str(ctx.guild.id)):
|
||||
shutil.rmtree("./tempMusic/" + str(ctx.guild.id))
|
||||
|
||||
@commands.command(name='summon')
|
||||
async def _summon(self, ctx: commands.Context, *, channel: nextcord.VoiceChannel = None):
|
||||
"""Summons the bot to a voice channel.
|
||||
If no channel was specified, it joins your channel.
|
||||
"""
|
||||
|
||||
if not channel and not ctx.author.voice:
|
||||
return await self.respond(ctx, 'You are neither connected to a voice channel nor specified a channel to join.')
|
||||
|
||||
destination = channel or ctx.author.voice.channel
|
||||
|
||||
# Check permission
|
||||
if not destination.permissions_for(ctx.me).connect:
|
||||
return await self.respond(ctx, "No permission to join the voice channel!")
|
||||
|
||||
if ctx.voice_state.voice:
|
||||
await self.respond(ctx, "Switched from **{}** to **{}**.".format(ctx.voice_state.voice.channel.name, destination.name))
|
||||
await ctx.voice_state.voice.move_to(destination)
|
||||
else:
|
||||
await self.respond(ctx, "Зашёл в **{}**.".format(destination.name))
|
||||
ctx.voice_state.voice = await destination.connect()
|
||||
if isinstance(ctx.author.voice.channel, nextcord.StageChannel):
|
||||
try:
|
||||
await asyncio.sleep(1)
|
||||
await ctx.me.edit(suppress=False)
|
||||
except:
|
||||
await self.respond(ctx, "У меня нет разрешения говорить! Пожалуйста, пригласите меня выступить!")
|
||||
|
||||
@commands.command(name='leave', aliases=['disconnect', 'dc'])
|
||||
async def _leave(self, ctx: commands.Context):
|
||||
"""Clears the queue and leaves the voice channel."""
|
||||
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
|
||||
if not ctx.voice_state.voice:
|
||||
return await self.respond(ctx, 'Not connected to any voice channel.')
|
||||
|
||||
await ctx.message.add_reaction('⏹')
|
||||
await ctx.voice_state.stop(leave=True)
|
||||
del self.voice_states[ctx.guild.id]
|
||||
|
||||
@commands.command(name='volume', aliases=['v'])
|
||||
async def _volume(self, ctx: commands.Context, *, volume: int):
|
||||
"""Sets the volume of the player."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
|
||||
if not ctx.voice_state:
|
||||
return await self.respond(ctx,'Not connected to any voice channel')
|
||||
|
||||
if 0 > volume or volume > 100:
|
||||
return await self.respond(ctx, 'Volume must be between 0 and 100')
|
||||
|
||||
ctx.voice_state.volume = volume / 100
|
||||
await self.respond(ctx, 'Volume of the player set to {}%'.format(volume))
|
||||
|
||||
@commands.command(name='now', aliases=['current', 'playing'])
|
||||
async def _now(self, ctx: commands.Context):
|
||||
"""Displays the currently playing song."""
|
||||
if(ctx.voice_state.current is None):
|
||||
return await self.respond(ctx, "There is no songs playing right now.")
|
||||
await self.respond(ctx, embed=ctx.voice_state.current.create_embed("now"))
|
||||
|
||||
@commands.command(name='pause')
|
||||
async def _pause(self, ctx: commands.Context):
|
||||
"""Pauses the currently playing song."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if ctx.voice_state.is_playing and ctx.voice_state.voice.is_playing():
|
||||
ctx.voice_state.voice.pause()
|
||||
await ctx.message.add_reaction('⏸')
|
||||
ctx.voice_state.current.pause_time = time.time()
|
||||
ctx.voice_state.current.paused = True
|
||||
else:
|
||||
await self.respond(ctx, "There is no songs playing right now.")
|
||||
|
||||
@commands.command(name='resume', aliases=['r'])
|
||||
async def _resume(self, ctx: commands.Context):
|
||||
"""Resumes a currently paused song."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if ctx.voice_state.is_playing and ctx.voice_state.voice.is_paused():
|
||||
ctx.voice_state.voice.resume()
|
||||
await ctx.message.add_reaction('▶')
|
||||
ctx.voice_state.current.pause_duration += time.time() - ctx.voice_state.current.pause_time
|
||||
ctx.voice_state.current.pause_time = 0
|
||||
ctx.voice_state.current.paused = False
|
||||
else:
|
||||
await self.respond(ctx, "There is no songs paused right now.")
|
||||
|
||||
@commands.command(name='stop')
|
||||
async def _stop(self, ctx: commands.Context):
|
||||
"""Stops playing song and clears the queue."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
ctx.voice_state.songs.clear()
|
||||
|
||||
if ctx.voice_state.is_playing:
|
||||
await ctx.voice_state.stop()
|
||||
ctx.voice_state.stopped = True
|
||||
await ctx.message.add_reaction('⏹')
|
||||
|
||||
@commands.command(name='skip', aliases=['s'])
|
||||
async def _skip(self, ctx: commands.Context):
|
||||
"""Vote to skip a song. The requester can automatically skip.
|
||||
3 skip votes are needed for the song to be skipped.
|
||||
"""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if not ctx.voice_state.is_playing:
|
||||
return await self.respond(ctx, 'Not playing any music right now...')
|
||||
|
||||
await ctx.message.add_reaction('⏭')
|
||||
ctx.voice_state.skip()
|
||||
|
||||
@commands.command(name='queue', aliases=["q"])
|
||||
async def _queue(self, ctx: commands.Context, *, page: int = 1):
|
||||
"""Shows the player's queue.
|
||||
You can optionally specify the page to show. Each page contains 10 elements.
|
||||
"""
|
||||
|
||||
if len(ctx.voice_state.songs) == 0:
|
||||
return await self.respond(ctx, 'Empty queue.')
|
||||
|
||||
items_per_page = 10
|
||||
pages = math.ceil(len(ctx.voice_state.songs) / items_per_page)
|
||||
|
||||
start = (page - 1) * items_per_page
|
||||
end = start + items_per_page
|
||||
|
||||
queue = ''
|
||||
for i, song in enumerate(ctx.voice_state.songs[start:end], start=start):
|
||||
if "local@" in song["url"]:
|
||||
queue += '`{0}.` **{1}**\n'.format(i + 1, song["title"].replace("_", "\\_"))
|
||||
else:
|
||||
queue += '`{0}.` [**{1[title]}**]({1[url]})\n'.format(i + 1, song)
|
||||
|
||||
await self.respond(ctx, embed=nextcord.Embed(description='**{} tracks:**\n\n{}'.format(len(ctx.voice_state.songs), queue))
|
||||
.set_footer(text='Viewing page {}/{}'.format(page, pages)))
|
||||
|
||||
@commands.command(name='shuffle')
|
||||
async def _shuffle(self, ctx: commands.Context):
|
||||
"""Shuffles the queue."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if len(ctx.voice_state.songs) == 0:
|
||||
return await self.respond(ctx,'Empty queue.')
|
||||
|
||||
ctx.voice_state.songs.shuffle()
|
||||
await ctx.message.add_reaction('🔀')
|
||||
|
||||
@commands.command(name='remove')
|
||||
async def _remove(self, ctx: commands.Context, index: int):
|
||||
"""Removes a song from the queue at a given index."""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if ctx.voice_state.voice.channel != ctx.author.voice.channel:
|
||||
return
|
||||
if len(ctx.voice_state.songs) == 0:
|
||||
return await self.respond(ctx,'Empty queue.')
|
||||
|
||||
ctx.voice_state.songs.remove(index - 1)
|
||||
await ctx.message.add_reaction('✅')
|
||||
|
||||
@commands.command(name='loop')
|
||||
async def _loop(self, ctx: commands.Context):
|
||||
"""Loops the currently playing song.
|
||||
Invoke this command again to unloop the song.
|
||||
"""
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if ctx.voice_state.voice.channel != ctx.author.voice.channel:
|
||||
return
|
||||
|
||||
# Inverse boolean value to loop and unloop.
|
||||
ctx.voice_state.loop = not ctx.voice_state.loop
|
||||
ctx.voice_state.ctx = ctx
|
||||
await self.respond(ctx,("Включен" if ctx.voice_state.loop else "Выключен") + " повтор воспроизведения")
|
||||
|
||||
@commands.command(name='play', aliases=["p"])
|
||||
async def _play(self, ctx: commands.Context, *, search: str):
|
||||
"""Plays a song.
|
||||
If there are songs in the queue, this will be queued until the
|
||||
other songs finished playing.
|
||||
This command automatically searches from various sites if no URL is provided.
|
||||
A list of these sites can be found here: https://rg3.github.io/youtube-dl/supportedsites.html
|
||||
"""
|
||||
|
||||
if not ctx.voice_state.voice:
|
||||
await ctx.invoke(self._join)
|
||||
if not ctx.voice_state.voice:
|
||||
return
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
|
||||
if ctx.voice_client:
|
||||
if ctx.voice_client.channel != ctx.author.voice.channel:
|
||||
return await self.respond(ctx, "Bot is already in a voice channel.")
|
||||
|
||||
loop = self.bot.loop
|
||||
try:
|
||||
await self.respond(ctx,'Ищу: **{}**'.format(search))
|
||||
if "/playlist?" in search:
|
||||
#import time
|
||||
#start_time = time.time()
|
||||
await self.respond(ctx,"Плейлист найден, пожалуйста, подождите, пока я получу данные плейлиста.")
|
||||
partial = functools.partial(YTDLSource.ytdl_playlist.extract_info, search, download=False)
|
||||
data = await loop.run_in_executor(None, partial)
|
||||
if data is None:
|
||||
raise YTDLError('Couldn\'t find anything that matches `{}`'.format(search))
|
||||
entries = data["entries"]
|
||||
playlist = []
|
||||
for pos, song in enumerate(entries):
|
||||
# Youtube only, guess no one would play other than Youtube, if yes, fuck off please
|
||||
url = "https://www.youtube.com/watch?v=" + song["id"]
|
||||
title = song["title"]
|
||||
playlist.append({"pos": pos, "url": url, "title": title})
|
||||
# Sort the playlist variable to match with the order in YouTube
|
||||
playlist.sort(key=lambda song: song["pos"])
|
||||
# Add all songs to the pending list
|
||||
for songs, entry in enumerate(playlist):
|
||||
await ctx.voice_state.songs.put({"url": entry["url"], "title": entry["title"], "user": ctx.author})
|
||||
await self.respond(ctx,' {} песен'.format(str(songs)))
|
||||
#await self.respond(ctx, 'Took {} to finish'.format(time.time()-start_time))
|
||||
else:
|
||||
# Just a normal song
|
||||
partial = functools.partial(YTDLSource.ytdl.extract_info, search, download=False)
|
||||
data = await loop.run_in_executor(None, partial)
|
||||
if "entries" in data:
|
||||
data = data["entries"][0]
|
||||
await ctx.voice_state.songs.put({"url": data["webpage_url"], "title": data["title"], "user": ctx.author})
|
||||
await self.respond(ctx,'В очереди: {}'.format(data["title"]))
|
||||
ctx.voice_state.stopped = False
|
||||
except YTDLError as e:
|
||||
await self.respond(ctx,'An error occurred while processing this request: {}'.format(str(e)))
|
||||
|
||||
@commands.command(name='search')
|
||||
async def search(self, ctx, *, keyword: str):
|
||||
originalkeyword = keyword
|
||||
keyword = "ytsearch10:" + keyword
|
||||
data = YTDLSource.ytdl_playlist.extract_info(keyword, download=False)
|
||||
result = []
|
||||
for entry in data["entries"]:
|
||||
result.append(
|
||||
{
|
||||
"title": entry.get("title"),
|
||||
"duration": YTDLSource.parse_duration(int(entry.get('duration'))),
|
||||
"url": entry.get('webpage_url', "https://www.youtube.com/watch?v=" + entry.get('id'))
|
||||
}
|
||||
)
|
||||
embed = nextcord.Embed( title=f'Ищу {originalkeyword}',
|
||||
description="Пожалуйста, выберите результат поиска, нажав эмодзи цифры",
|
||||
color=nextcord.Color.green())
|
||||
for count, entry in enumerate(result):
|
||||
embed.add_field(name=f'{count+1}. {entry["title"]}', value=f'[Link]({entry["url"]})' + "\nДлительность: " + entry["duration"] + "\n", inline=False)
|
||||
message = await self.respond(ctx,embed=embed)
|
||||
reaction_list = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"]
|
||||
for x in range(count + 1):
|
||||
await message.add_reaction(reaction_list[x])
|
||||
|
||||
def check(reaction, user):
|
||||
return user == ctx.message.author and str(reaction.emoji) in reaction_list and reaction.message == message
|
||||
|
||||
try:
|
||||
reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check)
|
||||
await message.edit(embed=nextcord.Embed(title="Selected:", description=result[reaction_list.index(reaction.emoji)]["title"], color=nextcord.Color.green()))
|
||||
if not ctx.author.voice or not ctx.author.voice.channel:
|
||||
return await self.respond(ctx,'Вы не подключены ни к одному голосовому каналу.')
|
||||
|
||||
if ctx.voice_client:
|
||||
if ctx.voice_client.channel != ctx.author.voice.channel:
|
||||
return await self.respond(ctx,'Бот уже находится в голосовом канале.')
|
||||
await self._play(ctx=ctx, search=result[reaction_list.index(reaction.emoji)]["url"])
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
await message.edit(embed=nextcord.Embed(title="Timed out"))
|
||||
await self.respond(ctx, "Истекло время ожидания, не получив никакого ответа...")
|
||||
|
||||
@commands.command(name='musicreload')
|
||||
async def musicreload(self, ctx):
|
||||
try:
|
||||
await ctx.voice_state.stop(leave=True)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
await ctx.voice_client.disconnect()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
await ctx.voice_client.clean_up()
|
||||
except:
|
||||
pass
|
||||
del self.voice_states[ctx.guild.id]
|
||||
await self.respond(ctx, "Музыкальный бот перезагрузился.")
|
||||
|
||||
@commands.command(name="loopqueue", aliases=['lq'])
|
||||
async def loopqueue(self, ctx):
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "Вы не подключены ни к одному голосовому каналу или к одному и тому же голосовому каналу.")
|
||||
|
||||
ctx.voice_state.loopqueue = not ctx.voice_state.loopqueue
|
||||
# The current song will also loop if loop queue enabled
|
||||
try:
|
||||
if ctx.voice_state.loopqueue:
|
||||
await ctx.voice_state.songs.put({"url": ctx.voice_state.current.source.url, "title": ctx.voice_state.current.source.title, "user": ctx.voice_state.current.source.requester})
|
||||
except:
|
||||
pass
|
||||
await self.respond(ctx,("Включен" if ctx.voice_state.loopqueue else "Выключено") + " зацикливание очереди")
|
||||
|
||||
@commands.command(name="playfile", aliases=["pf"])
|
||||
async def playfile(self, ctx, *, title=None):
|
||||
if not ctx.author.voice or not ctx.author.voice.channel or (ctx.voice_state.voice and ctx.author.voice.channel != ctx.voice_state.voice.channel):
|
||||
return await self.respond(ctx, "You are not connected to any voice channel or the same voice channel.")
|
||||
if len(ctx.message.attachments) == 0:
|
||||
return await self.respond(ctx, "Нет такого файла!")
|
||||
if not ctx.voice_state.voice:
|
||||
state = await ctx.invoke(self._join)
|
||||
if state:
|
||||
return
|
||||
import os
|
||||
if not os.path.isdir("./tempMusic"):
|
||||
os.mkdir("./tempMusic")
|
||||
if not os.path.isdir("./tempMusic/" + str(ctx.guild.id)):
|
||||
os.mkdir("./tempMusic/" + str(ctx.guild.id))
|
||||
filename = "./tempMusic/"+ str(ctx.guild.id) + "/" + str(int(time.time() * 10000000)) + "." + ctx.message.attachments[0].filename.split(".")[-1]
|
||||
await ctx.message.attachments[0].save(filename)
|
||||
if not title:
|
||||
title = ctx.message.attachments[0].filename
|
||||
await ctx.voice_state.songs.put({"url": "local@" + filename, "title": title, "user": ctx.author})
|
||||
await self.respond(ctx,'В очереди: {}'.format(title.replace("_", "\\_")))
|
||||
ctx.voice_state.stopped = False
|
||||
|
||||
@commands.command(name="runningservers", aliases=["rs"])
|
||||
async def runningservers(self, ctx):
|
||||
# Warning: If bot.author_id is not set, this will not work,
|
||||
# Therefore edit this line if needed
|
||||
if ctx.author.id == self.bot.author_id:
|
||||
server_count = 0
|
||||
desc = ""
|
||||
for voice_state in self.voice_states:
|
||||
if self.voice_states[voice_state].voice:
|
||||
guild = self.bot.get_guild(voice_state)
|
||||
server_count += 1
|
||||
desc += guild.name + "\n"
|
||||
return await self.respond(ctx, embed=nextcord.Embed(title="Серверы, на которых работает музыкальный бот: " + str(server_count), description=desc[:-1]))
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Music(bot))
|
|
@ -1,44 +0,0 @@
|
|||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
|
||||
|
||||
class muteandban(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def mute(self, ctx, member: nextcord.Member, *, reason=None):
|
||||
await member.move_to(channel=None)
|
||||
mute = nextcord.utils.get(ctx.guild.roles, name="Muted")
|
||||
await member.add_roles(mute)
|
||||
await ctx.send(f"**{ctx.author.name}** выдал мут **{member.display_name} по причине: {reason}**")
|
||||
|
||||
@commands.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def unmute(self, ctx, member: nextcord.Member):
|
||||
mute = nextcord.utils.get(ctx.guild.roles, name="Muted")
|
||||
await member.remove_roles(mute)
|
||||
await ctx.send(f"**{ctx.author.name}** размутил **{member.display_name}**")
|
||||
|
||||
@commands.command()
|
||||
@commands.has_permissions(ban_members=True)
|
||||
async def ban(self, ctx, member: nextcord.Member):
|
||||
if ctx.author.id != member.id:
|
||||
await member.ban(reason="не указана")
|
||||
else:
|
||||
await ctx.send("У вас нет прав для бана данного человека!", delete_after=4)
|
||||
|
||||
@commands.command()
|
||||
@commands.has_permissions(ban_members=True)
|
||||
async def unban(self, ctx, id):
|
||||
user = await self.bot.fetch_user(id)
|
||||
try:
|
||||
await ctx.guild.unban(user)
|
||||
except:
|
||||
await ctx.send("Пользователь не забанен", delete_after=4)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(muteandban(bot))
|
|
@ -6,4 +6,4 @@
|
|||
|
||||
[Добавить бота к себе на сервер](https://discord.com/api/oauth2/authorize?client_id=966669523031842856&permissions=8&scope=bot)
|
||||
|
||||
![image](https://git.disroot.org/CreeperXP/xpyct_bot/raw/commit/cbb26c949a50c077348d803a7fd2a17cfab1cc90/agplv3-with-text-162x68.png)
|
||||
![image](https://user-images.githubusercontent.com/67541714/164468688-10b1b574-55fd-4cab-8e36-b7a734b710a3.png)
|
346
main.py
346
main.py
|
@ -1,346 +0,0 @@
|
|||
#from webserver import keep_alive
|
||||
import os
|
||||
try:
|
||||
import nextcord
|
||||
except ImportError:
|
||||
print("Trying to Install required module: nextcord[voice]\n")
|
||||
os.system("python -m pip install --upgrade nextcord[voice]")
|
||||
import nextcord
|
||||
from nextcord.ext import commands
|
||||
import json
|
||||
import asyncio
|
||||
from urllib.parse import quote_plus
|
||||
from speedtest import Speedtest
|
||||
import string
|
||||
from discord_together import DiscordTogether
|
||||
|
||||
|
||||
def get_prefix(bot, message):
|
||||
with open("prefix.json", "r") as f:
|
||||
prefix = json.load(f)
|
||||
return prefix[str(message.guild.id)]
|
||||
|
||||
|
||||
PREFIX = "$"
|
||||
intents = nextcord.Intents().all()
|
||||
bot = commands.Bot(command_prefix=get_prefix, intents=intents)
|
||||
bot.remove_command("help")
|
||||
|
||||
version = "Bot v2.4"
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_guild_join(guild):
|
||||
with open("prefix.json", "r") as f:
|
||||
prefix = json.load(f)
|
||||
prefix[str(guild.id)] = "$"
|
||||
with open("prefix.json", "w") as f:
|
||||
json.dump(prefix, f, indent=4)
|
||||
|
||||
@bot.event
|
||||
async def on_guild_remove(guild):
|
||||
with open("prefix.json", "r") as f:
|
||||
prefix = json.load(f)
|
||||
prefix.pop(str(guild.id))
|
||||
with open("prefix.json", "w") as f:
|
||||
json.dump(prefix, f, indent=4)
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if {i.lower().translate(str.maketrans('','', string. punctuation)) for i in message.content.split(' ')}\
|
||||
.intersection(set(json.load(open('cenz.json')))) != set():
|
||||
await message.channel.send(f'{message.author.mention}, yyy… кого по губам отшлепать??')
|
||||
await message.delete()
|
||||
|
||||
await bot.process_commands(message)
|
||||
|
||||
@bot.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def setprefix(ctx, new: str):
|
||||
with open("prefix.json", "r") as f:
|
||||
prefix = json.load(f)
|
||||
prefix[str(ctx.guild.id)] = new
|
||||
with open("prefix.json", "w") as f:
|
||||
json.dump(prefix, f, indent=4)
|
||||
await ctx.send(f"Новый префикс `{new}`")
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
bot.togetherControl = await DiscordTogether("DISCORD_TOKEN")
|
||||
print(f"Bot logged as {bot.user} | {version}")
|
||||
await bot.change_presence(
|
||||
status=nextcord.Status.online, activity=nextcord.Game("$help")
|
||||
)
|
||||
|
||||
|
||||
@bot.command(pass_context=True)
|
||||
async def help(ctx):
|
||||
author = ctx.message.author
|
||||
|
||||
test_e = nextcord.Embed(
|
||||
colour=nextcord.Colour.orange()
|
||||
)
|
||||
test_e.set_author(name=f"Мои команды:")
|
||||
test_e.add_field(name="help", value="Помощь с командами", inline=False)
|
||||
test_e.add_field(name="ping", value="Отображение пинга")
|
||||
test_e.add_field(name="clear", value="(Либо purge или clean) очистка чата")
|
||||
test_e.add_field(name="hello", value="Бот поздоровается с вами")
|
||||
test_e.add_field(name="github", value="Ссылка на мой GitHub")
|
||||
test_e.add_field(name="ver", value="Версия бота")
|
||||
test_e.add_field(name="say", value="Пересказывание чего-либо от имени бота")
|
||||
test_e.add_field(name="serverinfo", value="Информация о сервере")
|
||||
test_e.add_field(name="send_m", value="Отправка сообщений в ЛС от имени бота")
|
||||
test_e.add_field(name="nitro", value="NOT a free nitro")
|
||||
test_e.add_field(name="guess", value="Угадай число")
|
||||
test_e.add_field(name="монета", value="Орёл или Решка?")
|
||||
test_e.add_field(name="play", value="Играет музыку. Пример: $play Never Gonna Give You Up")
|
||||
test_e.add_field(name="loop", value="Ставит повтор воспроизведения")
|
||||
test_e.add_field(name="stop", value="Остановка воспроизведения (НЕ ПАУЗА)")
|
||||
test_e.add_field(name="pause", value="Ставит воспроизведение на паузу")
|
||||
|
||||
await ctx.reply("Я отправил список команд тебе в ЛС", delete_after=5.0)
|
||||
await author.send(embed=test_e)
|
||||
|
||||
|
||||
@bot.command(
|
||||
aliases=[
|
||||
"Ping",
|
||||
"PING",
|
||||
"pING",
|
||||
"ping",
|
||||
"Пинг",
|
||||
"ПИНГ",
|
||||
"пИНГ",
|
||||
"пинг",
|
||||
"Понг",
|
||||
"ПОНГ",
|
||||
"пОНГ",
|
||||
"понг",
|
||||
]
|
||||
)
|
||||
async def __ping(
|
||||
ctx,
|
||||
): # Объявление асинхронной функции __ping с возможностью публикации сообщения
|
||||
ping = bot.ws.latency # Получаем пинг клиента
|
||||
|
||||
ping_emoji = "🟩🔳🔳🔳🔳" # Эмоция пинга, если он меньше 100ms
|
||||
|
||||
if ping > 0.10000000000000000:
|
||||
ping_emoji = "🟧🟩🔳🔳🔳" # Эмоция пинга, если он больше 100ms
|
||||
|
||||
if ping > 0.15000000000000000:
|
||||
ping_emoji = "🟥🟧🟩🔳🔳" # Эмоция пинга, если он больше 150ms
|
||||
|
||||
if ping > 0.20000000000000000:
|
||||
ping_emoji = "🟥🟥🟧🟩🔳" # Эмоция пинга, если он больше 200ms
|
||||
|
||||
if ping > 0.25000000000000000:
|
||||
ping_emoji = "🟥🟥🟥🟧🟩" # Эмоция пинга, если он больше 250ms
|
||||
|
||||
if ping > 0.30000000000000000:
|
||||
ping_emoji = "🟥🟥🟥🟥🟧" # Эмоция пинга, если он больше 300ms
|
||||
|
||||
if ping > 0.35000000000000000:
|
||||
ping_emoji = "🟥🟥🟥🟥🟥" # Эмоция пинга, если он больше 350ms
|
||||
|
||||
message = await ctx.send(
|
||||
"Пожалуйста, подождите. . ."
|
||||
) # Переменная message с первоначальным сообщением
|
||||
await message.edit(
|
||||
content=f"Понг! {ping_emoji} `{ping * 1000:.0f}ms` :ping_pong:"
|
||||
) # Редактирование первого сообщения на итоговое (На сам пинг)
|
||||
print(
|
||||
f"[Logs:utils] На данный момент пинг {ping * 1000:.0f}ms | {PREFIX}ping"
|
||||
) # Вывод пинга в консоль
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def hello(ctx):
|
||||
author = ctx.message.author
|
||||
await ctx.send(f"Привет, {author.mention}!")
|
||||
print(f"[Logs:utils] Приветствие было выведено | {PREFIX}hello")
|
||||
|
||||
@bot.command()
|
||||
async def link(ctx):
|
||||
await ctx.send("Ссылка на добавления бота на свой сервер: https://discord.com/api/oauth2/authorize?client_id=833720975069282344&permissions=0&scope=applications.commands%20bot")
|
||||
|
||||
@bot.command()
|
||||
async def ver(ctx):
|
||||
await ctx.send(f"{version} | Сделано CreeperXP#0363")
|
||||
print(f"[Logs:utils] Информация о боте была выведена | {PREFIX}ver")
|
||||
|
||||
|
||||
@bot.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def say(ctx, *, text):
|
||||
await ctx.message.delete
|
||||
await ctx.send(embed=nextcord.Embed(description=text))
|
||||
print(f"[Logs:utils] Сообщение пересказано ботом | {PREFIX}say")
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def serverinfo(ctx):
|
||||
role_count = len(ctx.guild.roles)
|
||||
|
||||
serverinfoEmbed = nextcord.Embed(color=ctx.author.color)
|
||||
|
||||
serverinfoEmbed.add_field(name="Название", value=f"{ctx.guild.name}", inline=False)
|
||||
serverinfoEmbed.add_field(
|
||||
name="Кол-во юзеров", value=f"{ctx.guild.member_count}", inline=False
|
||||
)
|
||||
serverinfoEmbed.add_field(
|
||||
name="Уровень безопасности",
|
||||
value=f"{ctx.guild.verification_level}",
|
||||
inline=False,
|
||||
)
|
||||
serverinfoEmbed.add_field(name="Кол-во ролей", value=f"{role_count}", inline=False)
|
||||
serverinfoEmbed.add_field(name="ID сервера", value=f"{ctx.guild.id}", inline=False)
|
||||
|
||||
await ctx.send(embed=serverinfoEmbed)
|
||||
|
||||
|
||||
@bot.command(aliases=["clear", "purge"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def clean(ctx, amount=None):
|
||||
await ctx.channel.purge(limit=int(amount) + 1)
|
||||
print(f"[Logs:utils] {amount} сообщений удалено | {PREFIX}clean")
|
||||
|
||||
|
||||
class Confirm(nextcord.ui.View):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.value = None
|
||||
|
||||
@nextcord.ui.button(label="Да", style=nextcord.ButtonStyle.danger)
|
||||
async def confirm(
|
||||
self, button: nextcord.ui.Button, interaction: nextcord.Interaction
|
||||
):
|
||||
await interaction.response.send_message(
|
||||
"https://github.com/CreeperXP/xpyct_bot", ephemeral=True
|
||||
)
|
||||
self.value = True
|
||||
self.stop()
|
||||
|
||||
@nextcord.ui.button(label="Нет", style=nextcord.ButtonStyle.blurple)
|
||||
async def cancel(
|
||||
self, button: nextcord.ui.Button, interaction: nextcord.Interaction
|
||||
):
|
||||
await interaction.response.send_message("Ну ладно :(", ephemeral=True)
|
||||
self.value = False
|
||||
self.stop()
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def github(ctx):
|
||||
view = Confirm()
|
||||
await ctx.send("Перейти на GiHub?", view=view)
|
||||
|
||||
await view.wait()
|
||||
|
||||
if not view.value == None:
|
||||
print("Timed Out")
|
||||
if view.value == True:
|
||||
print("Ссылка выведена")
|
||||
if view.value == False:
|
||||
print("Ссылка не выведена")
|
||||
print(f"[Logs:utils] Ссылка на GitHub была выведена | {PREFIX}github")
|
||||
|
||||
|
||||
@bot.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def send_m(ctx, member: nextcord.Member, *, text):
|
||||
await member.send(
|
||||
f"От {ctx.author.name}:", embed=nextcord.Embed(description=text)
|
||||
)
|
||||
print(
|
||||
f"[Logs:utils] Сообщение от {ctx.author.name} было отправлено {member.name} | {PREFIX}send_m"
|
||||
)
|
||||
|
||||
|
||||
class Google(nextcord.ui.View):
|
||||
def __init__(self, query: str):
|
||||
super().__init__()
|
||||
query = quote_plus(query)
|
||||
url = f"https://www.google.com/search?q={query}"
|
||||
self.add_item(nextcord.ui.Button(label="Google", url=url))
|
||||
|
||||
|
||||
class Yandex(nextcord.ui.View):
|
||||
def __init__(self, query: str):
|
||||
super().__init__()
|
||||
query = quote_plus(query)
|
||||
url = f"https://yandex.ua/search/?text={query}"
|
||||
self.add_item(nextcord.ui.Button(label="Яндекс", url=url))
|
||||
|
||||
|
||||
@bot.command(aliases=["поиск", "гугл"])
|
||||
async def google(ctx, *, query: str):
|
||||
await ctx.reply(f"Результаты запроса `{query}`", view=Google(query))
|
||||
|
||||
|
||||
@bot.command(aliases=["яндекс", "yndx"])
|
||||
async def yandex(ctx, *, query: str):
|
||||
await ctx.reply(f"Результаты запроса `{query}`", view=Yandex(query))
|
||||
|
||||
|
||||
st = Speedtest()
|
||||
dl_speed = int(st.download() / 8000)
|
||||
up_speed = int(st.upload() / 8000)
|
||||
ethernet_speed = (
|
||||
f"Скорость закачки: {dl_speed} kb/s \n" f"Скорость выгрузки: {up_speed} kb/s"
|
||||
)
|
||||
|
||||
|
||||
@bot.command(aliases=["esp"])
|
||||
async def ethspeed(ctx):
|
||||
await ctx.send(ethernet_speed)
|
||||
|
||||
|
||||
@bot.command(aliases=["нитро", "nitro"])
|
||||
async def freenitro(ctx):
|
||||
embed=nextcord.Embed(description=f"Click on the link and get a free nitro!\nhttps://clck.ru/9TFat")
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
|
||||
@bot.command(aliases=["youtube", "ютуб"])
|
||||
async def yt(ctx):
|
||||
link = await bot.togetherControl.create_link(ctx.author.voice.channel.id, 'youtube')
|
||||
await ctx.send(f"{link}")
|
||||
|
||||
@bot.command()
|
||||
async def timer(ctx, time: int): #таймер
|
||||
await ctx.send(f"Таймер запущен на {time} секунд")
|
||||
await asyncio.sleep(time)
|
||||
await ctx.send(f"Таймер завершен")
|
||||
|
||||
|
||||
# COGS
|
||||
|
||||
|
||||
@bot.command() ## Стандартное объявление комманды
|
||||
async def load(ctx, extensions): ## объявление функции
|
||||
bot.load_extension(f"cogs.{extensions}") ## загрузка доплонений
|
||||
await ctx.send("loaded")
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def unload(ctx, extensions):
|
||||
bot.unload_extension(f"cogs.{extensions}")
|
||||
await ctx.send("unloaded")
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def reload(ctx, extensions):
|
||||
bot.unload_extension(f"cogs.{extensions}") # отгружаем ког
|
||||
bot.load_extension(f"cogs.{extensions}") # загружаем
|
||||
await ctx.send("reloaded")
|
||||
|
||||
|
||||
for filename in os.listdir("./cogs"):
|
||||
if filename.endswith(".py"):
|
||||
bot.load_extension(f"cogs.{filename[:-3]}")
|
||||
|
||||
|
||||
#keep_alive()
|
||||
bot.run(os.getenv("DISCORD_TOKEN"))
|
|
@ -1,176 +0,0 @@
|
|||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "2.0.2"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=7.1.2"
|
||||
itsdangerous = ">=2.0"
|
||||
Jinja2 = ">=3.0"
|
||||
Werkzeug = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
async = ["asgiref (>=3.2)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.0.1"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.0.2"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.0.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "2.0.2"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[[package]]
|
||||
name = "youtube-dl"
|
||||
version = "2021.6.6"
|
||||
description = "YouTube video downloader"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "d45486ae9e1aac5681e67a74dc30de0690399b14ece14b517197a5e0adf8d41b"
|
||||
|
||||
[metadata.files]
|
||||
click = [
|
||||
{file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
|
||||
{file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
flask = [
|
||||
{file = "Flask-2.0.2-py3-none-any.whl", hash = "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"},
|
||||
{file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"},
|
||||
]
|
||||
itsdangerous = [
|
||||
{file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
|
||||
{file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
|
||||
{file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
|
||||
{file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
|
||||
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
||||
]
|
||||
werkzeug = [
|
||||
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
|
||||
{file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},
|
||||
]
|
||||
youtube-dl = [
|
||||
{file = "youtube_dl-2021.6.6-py2.py3-none-any.whl", hash = "sha256:263e04d53fb8ba3dfbd246ad09b7d388e896c132a20cc770c26ee7684de050ac"},
|
||||
{file = "youtube_dl-2021.6.6.tar.gz", hash = "sha256:cb2d3ee002158ede783e97a82c95f3817594df54367ea6a77ce5ceea4772f0ab"},
|
||||
]
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"893897417659920384": "$",
|
||||
"898553977816354877": "$",
|
||||
"820223886843052053": "$"
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
[tool.poetry]
|
||||
name = "repl_python3_beta_creeperbot"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["dima47452 <<>>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
Flask = "^2.0.2"
|
||||
youtube_dl = "^2021.6.6"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
|
@ -1,8 +0,0 @@
|
|||
async_timeout==4.0.2
|
||||
discord_together==1.2.1
|
||||
Flask==2.0.2
|
||||
nextcord==2.0.0a10
|
||||
requests==2.26.0
|
||||
speedtest==0.0.1
|
||||
speedtest_cli==2.1.3
|
||||
youtube_dl==2021.6.6
|
14
to_json.py
14
to_json.py
|
@ -1,14 +0,0 @@
|
|||
import json
|
||||
|
||||
ar = []
|
||||
|
||||
with open('cenz.txt', encoding='utf-8') as r:
|
||||
for i in r:
|
||||
n = i.lower().split('\n')[0]
|
||||
if n != '':
|
||||
ar.append(n)
|
||||
|
||||
with open('cenz.json', 'w', encoding='utf-8') as e:
|
||||
json.dump(ar, e)
|
||||
|
||||
#Run this file for generate the cenz.json
|
15
webserver.py
15
webserver.py
|
@ -1,15 +0,0 @@
|
|||
from flask import Flask
|
||||
from threading import Thread
|
||||
|
||||
app = Flask('')
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
return "Монитор активен. Больше ничего интересного"
|
||||
|
||||
def run():
|
||||
app.run(host='0.0.0.0',port=8080)
|
||||
|
||||
def keep_alive():
|
||||
t = Thread(target=run)
|
||||
t.start()
|
Loading…
Reference in New Issue