mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Added and updated repositories 2025-07-11 08:27:20
This commit is contained in:
@@ -1,145 +1,145 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class GameInfo(loader.Module):
|
||||
"""Module for fetching game information from RAWG"""
|
||||
|
||||
strings = {
|
||||
"name": "GameInfo",
|
||||
"game_not_found": "<b>❌ Game not found</b>",
|
||||
"fetching": "<b>🌐 Fetching game information...</b>",
|
||||
"no_api": "<b>❌ Please insert your api key in config</b> (<code>.cfg GameInfo</code>)",
|
||||
"error_fetching": "<b>❌ Error fetching game information</b>",
|
||||
"game": "<b><emoji document_id=5467583879948803288>🎮</emoji>Name: </b>%s",
|
||||
"release": (
|
||||
"<b><emoji document_id=5431897022456145283>📆</emoji>Data released: </b>%s"
|
||||
),
|
||||
"rawg_rating": (
|
||||
"<b><emoji document_id=5435957248314579621>⭐️</emoji>Rating: </b>%s"
|
||||
),
|
||||
"platforms": (
|
||||
"<b><emoji document_id=5386764531152198851>🏴☠️</emoji>Platforms: </b>%s"
|
||||
),
|
||||
"genres": "<b><emoji document_id=5188705588925702510>🎶</emoji>Genres: </b>%s",
|
||||
"screenshots": (
|
||||
"<b><emoji document_id=5818849313555483639>📸</emoji>Screenshots: </b>%s"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"game_not_found": "<b>❌ Игра не найдена</b>",
|
||||
"fetching": "<b>🌐 Получение информации об игре...</b>",
|
||||
"no_api": "<b>❌ Пожалуйста укажите api-ключ в конфиге (<code>.cfg GameInfo</code>)",
|
||||
"error_fetching": "<b>❌ Ошибка при получении информации об игре</b>",
|
||||
"game": "<b><emoji document_id=5467583879948803288>🎮</emoji>Название: </b>%s",
|
||||
"release": (
|
||||
"<b><emoji document_id=5431897022456145283>📆</emoji>Дата релиза: </b>%s"
|
||||
),
|
||||
"rawg_rating": (
|
||||
"<b><emoji document_id=5435957248314579621>⭐️</emoji>Рейтинг: </b>%s"
|
||||
),
|
||||
"platforms": (
|
||||
"<b><emoji document_id=5386764531152198851>🏴☠️</emoji>Платформы: </b>%s"
|
||||
),
|
||||
"genres": "<b><emoji document_id=5188705588925702510>🎶</emoji>Жанры: </b>%s",
|
||||
"screenshots": (
|
||||
"<b><emoji document_id=5818849313555483639>📸</emoji>Скриншоты: </b>%s"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
None,
|
||||
lambda: "Your API token from https://rawg.io/apidocs (If you are from Russia use VPN)",
|
||||
validator=loader.validators.Hidden(),
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command(ru_doc="Получить информацию об игре <название игры>")
|
||||
async def gameinfo(self, message: Message):
|
||||
"""Fetch game information from RAWG"""
|
||||
if self.config['api_key'] == None:
|
||||
await utils.answer(message, self.strings('no_api'))
|
||||
return
|
||||
|
||||
if not (game_name := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("game_not_found"))
|
||||
return
|
||||
|
||||
await utils.answer(message, self.strings("fetching"))
|
||||
|
||||
try:
|
||||
url = f"https://api.rawg.io/api/games?key={self.config['api_key']}&search={game_name}"
|
||||
response = await utils.run_sync(requests.get, url)
|
||||
|
||||
if response.status_code == 404:
|
||||
await utils.answer(message, self.strings("game_not_found"))
|
||||
return
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()["results"][0]
|
||||
|
||||
game_name = data.get("name", "N/A")
|
||||
released_date = data.get("released", "N/A")
|
||||
rating = data.get("rating", "N/A")
|
||||
|
||||
platforms_str = (
|
||||
", ".join(
|
||||
platform["platform"]["name"]
|
||||
for platform in data.get("platforms", [])
|
||||
)
|
||||
or "N/A"
|
||||
)
|
||||
|
||||
genres_str = (
|
||||
", ".join(genre["name"] for genre in data.get("genres", [])) or "N/A"
|
||||
)
|
||||
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
f"https://api.rawg.io/api/games/{data['id']}/screenshots?key={self.config['api_key']}",
|
||||
)
|
||||
screenshots = []
|
||||
|
||||
if response.status_code == 200:
|
||||
screenshots_data = response.json()["results"][:3]
|
||||
for screenshot in screenshots_data:
|
||||
screenshots.append(screenshot["image"])
|
||||
|
||||
screenshots_str = ", ".join(screenshots) if screenshots else "N/A"
|
||||
|
||||
game_info_message = (
|
||||
self.strings("game") % game_name
|
||||
+ "\n"
|
||||
+ self.strings("release") % released_date
|
||||
+ "\n"
|
||||
+ self.strings("rawg_rating") % rating
|
||||
+ "\n"
|
||||
+ self.strings("platforms") % platforms_str
|
||||
+ "\n"
|
||||
+ self.strings("genres") % genres_str
|
||||
+ "\n"
|
||||
+ self.strings("screenshots") % screenshots_str
|
||||
)
|
||||
await utils.answer(message, game_info_message)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_fetching"))
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class GameInfo(loader.Module):
|
||||
"""Module for fetching game information from RAWG"""
|
||||
|
||||
strings = {
|
||||
"name": "GameInfo",
|
||||
"game_not_found": "<b>❌ Game not found</b>",
|
||||
"fetching": "<b>🌐 Fetching game information...</b>",
|
||||
"no_api": "<b>❌ Please insert your api key in config</b> (<code>.cfg GameInfo</code>)",
|
||||
"error_fetching": "<b>❌ Error fetching game information</b>",
|
||||
"game": "<b><emoji document_id=5467583879948803288>🎮</emoji>Name: </b>%s",
|
||||
"release": (
|
||||
"<b><emoji document_id=5431897022456145283>📆</emoji>Data released: </b>%s"
|
||||
),
|
||||
"rawg_rating": (
|
||||
"<b><emoji document_id=5435957248314579621>⭐️</emoji>Rating: </b>%s"
|
||||
),
|
||||
"platforms": (
|
||||
"<b><emoji document_id=5386764531152198851>🏴☠️</emoji>Platforms: </b>%s"
|
||||
),
|
||||
"genres": "<b><emoji document_id=5188705588925702510>🎶</emoji>Genres: </b>%s",
|
||||
"screenshots": (
|
||||
"<b><emoji document_id=5818849313555483639>📸</emoji>Screenshots: </b>%s"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"game_not_found": "<b>❌ Игра не найдена</b>",
|
||||
"fetching": "<b>🌐 Получение информации об игре...</b>",
|
||||
"no_api": "<b>❌ Пожалуйста укажите api-ключ в конфиге (<code>.cfg GameInfo</code>)",
|
||||
"error_fetching": "<b>❌ Ошибка при получении информации об игре</b>",
|
||||
"game": "<b><emoji document_id=5467583879948803288>🎮</emoji>Название: </b>%s",
|
||||
"release": (
|
||||
"<b><emoji document_id=5431897022456145283>📆</emoji>Дата релиза: </b>%s"
|
||||
),
|
||||
"rawg_rating": (
|
||||
"<b><emoji document_id=5435957248314579621>⭐️</emoji>Рейтинг: </b>%s"
|
||||
),
|
||||
"platforms": (
|
||||
"<b><emoji document_id=5386764531152198851>🏴☠️</emoji>Платформы: </b>%s"
|
||||
),
|
||||
"genres": "<b><emoji document_id=5188705588925702510>🎶</emoji>Жанры: </b>%s",
|
||||
"screenshots": (
|
||||
"<b><emoji document_id=5818849313555483639>📸</emoji>Скриншоты: </b>%s"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
None,
|
||||
lambda: "Your API token from https://rawg.io/apidocs (If you are from Russia use VPN)",
|
||||
validator=loader.validators.Hidden(),
|
||||
)
|
||||
)
|
||||
|
||||
@loader.command(ru_doc="Получить информацию об игре <название игры>")
|
||||
async def gameinfo(self, message: Message):
|
||||
"""Fetch game information from RAWG"""
|
||||
if self.config['api_key'] == None:
|
||||
await utils.answer(message, self.strings('no_api'))
|
||||
return
|
||||
|
||||
if not (game_name := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("game_not_found"))
|
||||
return
|
||||
|
||||
await utils.answer(message, self.strings("fetching"))
|
||||
|
||||
try:
|
||||
url = f"https://api.rawg.io/api/games?key={self.config['api_key']}&search={game_name}"
|
||||
response = await utils.run_sync(requests.get, url)
|
||||
|
||||
if response.status_code == 404:
|
||||
await utils.answer(message, self.strings("game_not_found"))
|
||||
return
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()["results"][0]
|
||||
|
||||
game_name = data.get("name", "N/A")
|
||||
released_date = data.get("released", "N/A")
|
||||
rating = data.get("rating", "N/A")
|
||||
|
||||
platforms_str = (
|
||||
", ".join(
|
||||
platform["platform"]["name"]
|
||||
for platform in data.get("platforms", [])
|
||||
)
|
||||
or "N/A"
|
||||
)
|
||||
|
||||
genres_str = (
|
||||
", ".join(genre["name"] for genre in data.get("genres", [])) or "N/A"
|
||||
)
|
||||
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
f"https://api.rawg.io/api/games/{data['id']}/screenshots?key={self.config['api_key']}",
|
||||
)
|
||||
screenshots = []
|
||||
|
||||
if response.status_code == 200:
|
||||
screenshots_data = response.json()["results"][:3]
|
||||
for screenshot in screenshots_data:
|
||||
screenshots.append(screenshot["image"])
|
||||
|
||||
screenshots_str = ", ".join(screenshots) if screenshots else "N/A"
|
||||
|
||||
game_info_message = (
|
||||
self.strings("game") % game_name
|
||||
+ "\n"
|
||||
+ self.strings("release") % released_date
|
||||
+ "\n"
|
||||
+ self.strings("rawg_rating") % rating
|
||||
+ "\n"
|
||||
+ self.strings("platforms") % platforms_str
|
||||
+ "\n"
|
||||
+ self.strings("genres") % genres_str
|
||||
+ "\n"
|
||||
+ self.strings("screenshots") % screenshots_str
|
||||
)
|
||||
await utils.answer(message, game_info_message)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_fetching"))
|
||||
|
||||
@@ -1,350 +1,350 @@
|
||||
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
|
||||
International
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright and
|
||||
certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are intended for use
|
||||
by those authorized to give the public permission to use material in
|
||||
ways otherwise restricted by copyright and certain other rights. Our
|
||||
licenses are irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it. Licensors
|
||||
should also secure all rights necessary before applying our licenses so
|
||||
that the public can reuse the material as expected. Licensors should
|
||||
clearly mark any material not subject to the license. This includes
|
||||
other CC-licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors :
|
||||
wiki.creativecommons.org/Considerations\_for\_licensors
|
||||
|
||||
Considerations for the public: By using one of our public licenses, a
|
||||
licensor grants the public permission to use the licensed material under
|
||||
specified terms and conditions. If the licensor's permission is not
|
||||
necessary for any reason–for example, because of any applicable
|
||||
exception or limitation to copyright–then that use is not regulated by
|
||||
the license. Our licenses grant only permissions under copyright and
|
||||
certain other rights that a licensor has authority to grant. Use of the
|
||||
licensed material may still be restricted for other reasons, including
|
||||
because others have copyright or other rights in the material. A
|
||||
licensor may make special requests, such as asking that all changes be
|
||||
marked or described. Although not required by our licenses, you are
|
||||
encouraged to respect those requests where reasonable. More
|
||||
considerations for the public :
|
||||
wiki.creativecommons.org/Considerations\_for\_licensees
|
||||
|
||||
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
|
||||
International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-NonCommercial-NoDerivatives 4.0 International Public License
|
||||
("Public License"). To the extent this Public License may be interpreted
|
||||
as a contract, You are granted the Licensed Rights in consideration of
|
||||
Your acceptance of these terms and conditions, and the Licensor grants
|
||||
You such rights in consideration of benefits the Licensor receives from
|
||||
making the Licensed Material available under these terms and conditions.
|
||||
|
||||
- Section 1 – Definitions.
|
||||
|
||||
- a. Adapted Material means material subject to Copyright and
|
||||
Similar Rights that is derived from or based upon the Licensed
|
||||
Material and in which the Licensed Material is translated,
|
||||
altered, arranged, transformed, or otherwise modified in a
|
||||
manner requiring permission under the Copyright and Similar
|
||||
Rights held by the Licensor. For purposes of this Public
|
||||
License, where the Licensed Material is a musical work,
|
||||
performance, or sound recording, Adapted Material is always
|
||||
produced where the Licensed Material is synched in timed
|
||||
relation with a moving image.
|
||||
- b. Copyright and Similar Rights means copyright and/or similar
|
||||
rights closely related to copyright including, without
|
||||
limitation, performance, broadcast, sound recording, and Sui
|
||||
Generis Database Rights, without regard to how the rights are
|
||||
labeled or categorized. For purposes of this Public License, the
|
||||
rights specified in Section 2(b)(1)-(2) are not Copyright and
|
||||
Similar Rights.
|
||||
- c. Effective Technological Measures means those measures that,
|
||||
in the absence of proper authority, may not be circumvented
|
||||
under laws fulfilling obligations under Article 11 of the WIPO
|
||||
Copyright Treaty adopted on December 20, 1996, and/or similar
|
||||
international agreements.
|
||||
- d. Exceptions and Limitations means fair use, fair dealing,
|
||||
and/or any other exception or limitation to Copyright and
|
||||
Similar Rights that applies to Your use of the Licensed
|
||||
Material.
|
||||
- e. Licensed Material means the artistic or literary work,
|
||||
database, or other material to which the Licensor applied this
|
||||
Public License.
|
||||
- f. Licensed Rights means the rights granted to You subject to
|
||||
the terms and conditions of this Public License, which are
|
||||
limited to all Copyright and Similar Rights that apply to Your
|
||||
use of the Licensed Material and that the Licensor has authority
|
||||
to license.
|
||||
- g. Licensor means the individual(s) or entity(ies) granting
|
||||
rights under this Public License.
|
||||
- h. NonCommercial means not primarily intended for or directed
|
||||
towards commercial advantage or monetary compensation. For
|
||||
purposes of this Public License, the exchange of the Licensed
|
||||
Material for other material subject to Copyright and Similar
|
||||
Rights by digital file-sharing or similar means is NonCommercial
|
||||
provided there is no payment of monetary compensation in
|
||||
connection with the exchange.
|
||||
- i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance,
|
||||
distribution, dissemination, communication, or importation, and
|
||||
to make material available to the public including in ways that
|
||||
members of the public may access the material from a place and
|
||||
at a time individually chosen by them.
|
||||
- j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and
|
||||
of the Council of 11 March 1996 on the legal protection of
|
||||
databases, as amended and/or succeeded, as well as other
|
||||
essentially equivalent rights anywhere in the world.
|
||||
- k. You means the individual or entity exercising the Licensed
|
||||
Rights under this Public License. Your has a corresponding
|
||||
meaning.
|
||||
|
||||
- Section 2 – Scope.
|
||||
|
||||
- a. License grant.
|
||||
- 1. Subject to the terms and conditions of this Public
|
||||
License, the Licensor hereby grants You a worldwide,
|
||||
royalty-free, non-sublicensable, non-exclusive, irrevocable
|
||||
license to exercise the Licensed Rights in the Licensed
|
||||
Material to:
|
||||
- A. reproduce and Share the Licensed Material, in whole
|
||||
or in part, for NonCommercial purposes only; and
|
||||
- B. produce and reproduce, but not Share, Adapted
|
||||
Material for NonCommercial purposes only.
|
||||
- 2. Exceptions and Limitations. For the avoidance of doubt,
|
||||
where Exceptions and Limitations apply to Your use, this
|
||||
Public License does not apply, and You do not need to comply
|
||||
with its terms and conditions.
|
||||
- 3. Term. The term of this Public License is specified in
|
||||
Section 6(a).
|
||||
- 4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter
|
||||
created, and to make technical modifications necessary to do
|
||||
so. The Licensor waives and/or agrees not to assert any
|
||||
right or authority to forbid You from making technical
|
||||
modifications necessary to exercise the Licensed Rights,
|
||||
including technical modifications necessary to circumvent
|
||||
Effective Technological Measures. For purposes of this
|
||||
Public License, simply making modifications authorized by
|
||||
this Section 2(a)(4) never produces Adapted Material.
|
||||
- 5. Downstream recipients.
|
||||
- A. Offer from the Licensor – Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
- B. No downstream restrictions. You may not offer or
|
||||
impose any additional or different terms or conditions
|
||||
on, or apply any Effective Technological Measures to,
|
||||
the Licensed Material if doing so restricts exercise of
|
||||
the Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
- 6. No endorsement. Nothing in this Public License
|
||||
constitutes or may be construed as permission to assert or
|
||||
imply that You are, or that Your use of the Licensed
|
||||
Material is, connected with, or sponsored, endorsed, or
|
||||
granted official status by, the Licensor or others
|
||||
designated to receive attribution as provided in Section
|
||||
3(a)(1)(A)(i).
|
||||
- b. Other rights.
|
||||
- 1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however,
|
||||
to the extent possible, the Licensor waives and/or agrees
|
||||
not to assert any such rights held by the Licensor to the
|
||||
limited extent necessary to allow You to exercise the
|
||||
Licensed Rights, but not otherwise.
|
||||
- 2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
- 3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties, including when
|
||||
the Licensed Material is used other than for NonCommercial
|
||||
purposes.
|
||||
|
||||
- Section 3 – License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to
|
||||
the following conditions.
|
||||
|
||||
- a. Attribution.
|
||||
- 1. If You Share the Licensed Material, You must:
|
||||
|
||||
- A. retain the following if it is supplied by the
|
||||
Licensor with the Licensed Material:
|
||||
- i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if designated);
|
||||
- ii. a copyright notice;
|
||||
- iii. a notice that refers to this Public License;
|
||||
- iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
- v. a URI or hyperlink to the Licensed Material to
|
||||
the extent reasonably practicable;
|
||||
- B. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
- C. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
For the avoidance of doubt, You do not have permission under
|
||||
this Public License to Share Adapted Material.
|
||||
|
||||
- 2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may
|
||||
be reasonable to satisfy the conditions by providing a URI
|
||||
or hyperlink to a resource that includes the required
|
||||
information.
|
||||
- 3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
- Section 4 – Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
- a. for the avoidance of doubt, Section 2(a)(1) grants You the
|
||||
right to extract, reuse, reproduce, and Share all or a
|
||||
substantial portion of the contents of the database for
|
||||
NonCommercial purposes only and provided You do not Share
|
||||
Adapted Material;
|
||||
- b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material;
|
||||
and
|
||||
- c. You must comply with the conditions in Section 3(a) if You
|
||||
Share all or a substantial portion of the contents of the
|
||||
database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the
|
||||
Licensed Rights include other Copyright and Similar Rights.
|
||||
|
||||
- Section 5 – Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
- a. Unless otherwise separately undertaken by the Licensor, to
|
||||
the extent possible, the Licensor offers the Licensed Material
|
||||
as-is and as-available, and makes no representations or
|
||||
warranties of any kind concerning the Licensed Material, whether
|
||||
express, implied, statutory, or other. This includes, without
|
||||
limitation, warranties of title, merchantability, fitness for a
|
||||
particular purpose, non-infringement, absence of latent or other
|
||||
defects, accuracy, or the presence or absence of errors, whether
|
||||
or not known or discoverable. Where disclaimers of warranties
|
||||
are not allowed in full or in part, this disclaimer may not
|
||||
apply to You.
|
||||
- b. To the extent possible, in no event will the Licensor be
|
||||
liable to You on any legal theory (including, without
|
||||
limitation, negligence) or otherwise for any direct, special,
|
||||
indirect, incidental, consequential, punitive, exemplary, or
|
||||
other losses, costs, expenses, or damages arising out of this
|
||||
Public License or use of the Licensed Material, even if the
|
||||
Licensor has been advised of the possibility of such losses,
|
||||
costs, expenses, or damages. Where a limitation of liability is
|
||||
not allowed in full or in part, this limitation may not apply to
|
||||
You.
|
||||
- c. The disclaimer of warranties and limitation of liability
|
||||
provided above shall be interpreted in a manner that, to the
|
||||
extent possible, most closely approximates an absolute
|
||||
disclaimer and waiver of all liability.
|
||||
|
||||
- Section 6 – Term and Termination.
|
||||
|
||||
- a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply
|
||||
with this Public License, then Your rights under this Public
|
||||
License terminate automatically.
|
||||
- b. Where Your right to use the Licensed Material has terminated
|
||||
under Section 6(a), it reinstates:
|
||||
|
||||
- 1. automatically as of the date the violation is cured,
|
||||
provided it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
- 2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect
|
||||
any right the Licensor may have to seek remedies for Your
|
||||
violations of this Public License.
|
||||
|
||||
- c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing
|
||||
so will not terminate this Public License.
|
||||
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
- Section 7 – Other Terms and Conditions.
|
||||
|
||||
- a. The Licensor shall not be bound by any additional or
|
||||
different terms or conditions communicated by You unless
|
||||
expressly agreed.
|
||||
- b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
- Section 8 – Interpretation.
|
||||
|
||||
- a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could
|
||||
lawfully be made without permission under this Public License.
|
||||
- b. To the extent possible, if any provision of this Public
|
||||
License is deemed unenforceable, it shall be automatically
|
||||
reformed to the minimum extent necessary to make it enforceable.
|
||||
If the provision cannot be reformed, it shall be severed from
|
||||
this Public License without affecting the enforceability of the
|
||||
remaining terms and conditions.
|
||||
- c. No term or condition of this Public License will be waived
|
||||
and no failure to comply consented to unless expressly agreed to
|
||||
by the Licensor.
|
||||
- d. Nothing in this Public License constitutes or may be
|
||||
interpreted as a limitation upon, or waiver of, any privileges
|
||||
and immunities that apply to the Licensor or You, including from
|
||||
the legal processes of any jurisdiction or authority.
|
||||
|
||||
Creative Commons is not a party to its public licenses. Notwithstanding,
|
||||
Creative Commons may elect to apply one of its public licenses to
|
||||
material it publishes and in those instances will be considered the
|
||||
"Licensor." The text of the Creative Commons public licenses is
|
||||
dedicated to the public domain under the CC0 Public Domain Dedication.
|
||||
Except for the limited purpose of indicating that material is shared
|
||||
under a Creative Commons public license or as otherwise permitted by the
|
||||
Creative Commons policies published at creativecommons.org/policies,
|
||||
Creative Commons does not authorize the use of the trademark "Creative
|
||||
Commons" or any other trademark or logo of Creative Commons without its
|
||||
prior written consent including, without limitation, in connection with
|
||||
any unauthorized modifications to any of its public licenses or any
|
||||
other arrangements, understandings, or agreements concerning use of
|
||||
licensed material. For the avoidance of doubt, this paragraph does not
|
||||
form part of the public licenses.
|
||||
|
||||
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
|
||||
International
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright and
|
||||
certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are intended for use
|
||||
by those authorized to give the public permission to use material in
|
||||
ways otherwise restricted by copyright and certain other rights. Our
|
||||
licenses are irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it. Licensors
|
||||
should also secure all rights necessary before applying our licenses so
|
||||
that the public can reuse the material as expected. Licensors should
|
||||
clearly mark any material not subject to the license. This includes
|
||||
other CC-licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors :
|
||||
wiki.creativecommons.org/Considerations\_for\_licensors
|
||||
|
||||
Considerations for the public: By using one of our public licenses, a
|
||||
licensor grants the public permission to use the licensed material under
|
||||
specified terms and conditions. If the licensor's permission is not
|
||||
necessary for any reason–for example, because of any applicable
|
||||
exception or limitation to copyright–then that use is not regulated by
|
||||
the license. Our licenses grant only permissions under copyright and
|
||||
certain other rights that a licensor has authority to grant. Use of the
|
||||
licensed material may still be restricted for other reasons, including
|
||||
because others have copyright or other rights in the material. A
|
||||
licensor may make special requests, such as asking that all changes be
|
||||
marked or described. Although not required by our licenses, you are
|
||||
encouraged to respect those requests where reasonable. More
|
||||
considerations for the public :
|
||||
wiki.creativecommons.org/Considerations\_for\_licensees
|
||||
|
||||
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
|
||||
International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-NonCommercial-NoDerivatives 4.0 International Public License
|
||||
("Public License"). To the extent this Public License may be interpreted
|
||||
as a contract, You are granted the Licensed Rights in consideration of
|
||||
Your acceptance of these terms and conditions, and the Licensor grants
|
||||
You such rights in consideration of benefits the Licensor receives from
|
||||
making the Licensed Material available under these terms and conditions.
|
||||
|
||||
- Section 1 – Definitions.
|
||||
|
||||
- a. Adapted Material means material subject to Copyright and
|
||||
Similar Rights that is derived from or based upon the Licensed
|
||||
Material and in which the Licensed Material is translated,
|
||||
altered, arranged, transformed, or otherwise modified in a
|
||||
manner requiring permission under the Copyright and Similar
|
||||
Rights held by the Licensor. For purposes of this Public
|
||||
License, where the Licensed Material is a musical work,
|
||||
performance, or sound recording, Adapted Material is always
|
||||
produced where the Licensed Material is synched in timed
|
||||
relation with a moving image.
|
||||
- b. Copyright and Similar Rights means copyright and/or similar
|
||||
rights closely related to copyright including, without
|
||||
limitation, performance, broadcast, sound recording, and Sui
|
||||
Generis Database Rights, without regard to how the rights are
|
||||
labeled or categorized. For purposes of this Public License, the
|
||||
rights specified in Section 2(b)(1)-(2) are not Copyright and
|
||||
Similar Rights.
|
||||
- c. Effective Technological Measures means those measures that,
|
||||
in the absence of proper authority, may not be circumvented
|
||||
under laws fulfilling obligations under Article 11 of the WIPO
|
||||
Copyright Treaty adopted on December 20, 1996, and/or similar
|
||||
international agreements.
|
||||
- d. Exceptions and Limitations means fair use, fair dealing,
|
||||
and/or any other exception or limitation to Copyright and
|
||||
Similar Rights that applies to Your use of the Licensed
|
||||
Material.
|
||||
- e. Licensed Material means the artistic or literary work,
|
||||
database, or other material to which the Licensor applied this
|
||||
Public License.
|
||||
- f. Licensed Rights means the rights granted to You subject to
|
||||
the terms and conditions of this Public License, which are
|
||||
limited to all Copyright and Similar Rights that apply to Your
|
||||
use of the Licensed Material and that the Licensor has authority
|
||||
to license.
|
||||
- g. Licensor means the individual(s) or entity(ies) granting
|
||||
rights under this Public License.
|
||||
- h. NonCommercial means not primarily intended for or directed
|
||||
towards commercial advantage or monetary compensation. For
|
||||
purposes of this Public License, the exchange of the Licensed
|
||||
Material for other material subject to Copyright and Similar
|
||||
Rights by digital file-sharing or similar means is NonCommercial
|
||||
provided there is no payment of monetary compensation in
|
||||
connection with the exchange.
|
||||
- i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance,
|
||||
distribution, dissemination, communication, or importation, and
|
||||
to make material available to the public including in ways that
|
||||
members of the public may access the material from a place and
|
||||
at a time individually chosen by them.
|
||||
- j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and
|
||||
of the Council of 11 March 1996 on the legal protection of
|
||||
databases, as amended and/or succeeded, as well as other
|
||||
essentially equivalent rights anywhere in the world.
|
||||
- k. You means the individual or entity exercising the Licensed
|
||||
Rights under this Public License. Your has a corresponding
|
||||
meaning.
|
||||
|
||||
- Section 2 – Scope.
|
||||
|
||||
- a. License grant.
|
||||
- 1. Subject to the terms and conditions of this Public
|
||||
License, the Licensor hereby grants You a worldwide,
|
||||
royalty-free, non-sublicensable, non-exclusive, irrevocable
|
||||
license to exercise the Licensed Rights in the Licensed
|
||||
Material to:
|
||||
- A. reproduce and Share the Licensed Material, in whole
|
||||
or in part, for NonCommercial purposes only; and
|
||||
- B. produce and reproduce, but not Share, Adapted
|
||||
Material for NonCommercial purposes only.
|
||||
- 2. Exceptions and Limitations. For the avoidance of doubt,
|
||||
where Exceptions and Limitations apply to Your use, this
|
||||
Public License does not apply, and You do not need to comply
|
||||
with its terms and conditions.
|
||||
- 3. Term. The term of this Public License is specified in
|
||||
Section 6(a).
|
||||
- 4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter
|
||||
created, and to make technical modifications necessary to do
|
||||
so. The Licensor waives and/or agrees not to assert any
|
||||
right or authority to forbid You from making technical
|
||||
modifications necessary to exercise the Licensed Rights,
|
||||
including technical modifications necessary to circumvent
|
||||
Effective Technological Measures. For purposes of this
|
||||
Public License, simply making modifications authorized by
|
||||
this Section 2(a)(4) never produces Adapted Material.
|
||||
- 5. Downstream recipients.
|
||||
- A. Offer from the Licensor – Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
- B. No downstream restrictions. You may not offer or
|
||||
impose any additional or different terms or conditions
|
||||
on, or apply any Effective Technological Measures to,
|
||||
the Licensed Material if doing so restricts exercise of
|
||||
the Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
- 6. No endorsement. Nothing in this Public License
|
||||
constitutes or may be construed as permission to assert or
|
||||
imply that You are, or that Your use of the Licensed
|
||||
Material is, connected with, or sponsored, endorsed, or
|
||||
granted official status by, the Licensor or others
|
||||
designated to receive attribution as provided in Section
|
||||
3(a)(1)(A)(i).
|
||||
- b. Other rights.
|
||||
- 1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however,
|
||||
to the extent possible, the Licensor waives and/or agrees
|
||||
not to assert any such rights held by the Licensor to the
|
||||
limited extent necessary to allow You to exercise the
|
||||
Licensed Rights, but not otherwise.
|
||||
- 2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
- 3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties, including when
|
||||
the Licensed Material is used other than for NonCommercial
|
||||
purposes.
|
||||
|
||||
- Section 3 – License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to
|
||||
the following conditions.
|
||||
|
||||
- a. Attribution.
|
||||
- 1. If You Share the Licensed Material, You must:
|
||||
|
||||
- A. retain the following if it is supplied by the
|
||||
Licensor with the Licensed Material:
|
||||
- i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if designated);
|
||||
- ii. a copyright notice;
|
||||
- iii. a notice that refers to this Public License;
|
||||
- iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
- v. a URI or hyperlink to the Licensed Material to
|
||||
the extent reasonably practicable;
|
||||
- B. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
- C. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
For the avoidance of doubt, You do not have permission under
|
||||
this Public License to Share Adapted Material.
|
||||
|
||||
- 2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may
|
||||
be reasonable to satisfy the conditions by providing a URI
|
||||
or hyperlink to a resource that includes the required
|
||||
information.
|
||||
- 3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
- Section 4 – Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
- a. for the avoidance of doubt, Section 2(a)(1) grants You the
|
||||
right to extract, reuse, reproduce, and Share all or a
|
||||
substantial portion of the contents of the database for
|
||||
NonCommercial purposes only and provided You do not Share
|
||||
Adapted Material;
|
||||
- b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material;
|
||||
and
|
||||
- c. You must comply with the conditions in Section 3(a) if You
|
||||
Share all or a substantial portion of the contents of the
|
||||
database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the
|
||||
Licensed Rights include other Copyright and Similar Rights.
|
||||
|
||||
- Section 5 – Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
- a. Unless otherwise separately undertaken by the Licensor, to
|
||||
the extent possible, the Licensor offers the Licensed Material
|
||||
as-is and as-available, and makes no representations or
|
||||
warranties of any kind concerning the Licensed Material, whether
|
||||
express, implied, statutory, or other. This includes, without
|
||||
limitation, warranties of title, merchantability, fitness for a
|
||||
particular purpose, non-infringement, absence of latent or other
|
||||
defects, accuracy, or the presence or absence of errors, whether
|
||||
or not known or discoverable. Where disclaimers of warranties
|
||||
are not allowed in full or in part, this disclaimer may not
|
||||
apply to You.
|
||||
- b. To the extent possible, in no event will the Licensor be
|
||||
liable to You on any legal theory (including, without
|
||||
limitation, negligence) or otherwise for any direct, special,
|
||||
indirect, incidental, consequential, punitive, exemplary, or
|
||||
other losses, costs, expenses, or damages arising out of this
|
||||
Public License or use of the Licensed Material, even if the
|
||||
Licensor has been advised of the possibility of such losses,
|
||||
costs, expenses, or damages. Where a limitation of liability is
|
||||
not allowed in full or in part, this limitation may not apply to
|
||||
You.
|
||||
- c. The disclaimer of warranties and limitation of liability
|
||||
provided above shall be interpreted in a manner that, to the
|
||||
extent possible, most closely approximates an absolute
|
||||
disclaimer and waiver of all liability.
|
||||
|
||||
- Section 6 – Term and Termination.
|
||||
|
||||
- a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply
|
||||
with this Public License, then Your rights under this Public
|
||||
License terminate automatically.
|
||||
- b. Where Your right to use the Licensed Material has terminated
|
||||
under Section 6(a), it reinstates:
|
||||
|
||||
- 1. automatically as of the date the violation is cured,
|
||||
provided it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
- 2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect
|
||||
any right the Licensor may have to seek remedies for Your
|
||||
violations of this Public License.
|
||||
|
||||
- c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing
|
||||
so will not terminate this Public License.
|
||||
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
- Section 7 – Other Terms and Conditions.
|
||||
|
||||
- a. The Licensor shall not be bound by any additional or
|
||||
different terms or conditions communicated by You unless
|
||||
expressly agreed.
|
||||
- b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
- Section 8 – Interpretation.
|
||||
|
||||
- a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could
|
||||
lawfully be made without permission under this Public License.
|
||||
- b. To the extent possible, if any provision of this Public
|
||||
License is deemed unenforceable, it shall be automatically
|
||||
reformed to the minimum extent necessary to make it enforceable.
|
||||
If the provision cannot be reformed, it shall be severed from
|
||||
this Public License without affecting the enforceability of the
|
||||
remaining terms and conditions.
|
||||
- c. No term or condition of this Public License will be waived
|
||||
and no failure to comply consented to unless expressly agreed to
|
||||
by the Licensor.
|
||||
- d. Nothing in this Public License constitutes or may be
|
||||
interpreted as a limitation upon, or waiver of, any privileges
|
||||
and immunities that apply to the Licensor or You, including from
|
||||
the legal processes of any jurisdiction or authority.
|
||||
|
||||
Creative Commons is not a party to its public licenses. Notwithstanding,
|
||||
Creative Commons may elect to apply one of its public licenses to
|
||||
material it publishes and in those instances will be considered the
|
||||
"Licensor." The text of the Creative Commons public licenses is
|
||||
dedicated to the public domain under the CC0 Public Domain Dedication.
|
||||
Except for the limited purpose of indicating that material is shared
|
||||
under a Creative Commons public license or as otherwise permitted by the
|
||||
Creative Commons policies published at creativecommons.org/policies,
|
||||
Creative Commons does not authorize the use of the trademark "Creative
|
||||
Commons" or any other trademark or logo of Creative Commons without its
|
||||
prior written consent including, without limitation, in connection with
|
||||
any unauthorized modifications to any of its public licenses or any
|
||||
other arrangements, understandings, or agreements concerning use of
|
||||
licensed material. For the avoidance of doubt, this paragraph does not
|
||||
form part of the public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
@@ -1,118 +1,118 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# scope: ffmpeg
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ClownModule(loader.Module):
|
||||
"""Модуль для клоунизации 'pov - <username>'"""
|
||||
|
||||
strings = {
|
||||
"name": "ClownMod",
|
||||
"video_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Error inside module."
|
||||
" (video_not_found)</b>"
|
||||
),
|
||||
"processing": (
|
||||
"<b><emoji document_id=5334643333488713810>🌐</emoji>Processing"
|
||||
" video...</b>"
|
||||
),
|
||||
"sending": (
|
||||
"<b><emoji document_id=5371074117971745503>🤡</emoji>Sending video...</b>"
|
||||
),
|
||||
"error_downloading": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Error inside module."
|
||||
" (error_downloading)</b>"
|
||||
),
|
||||
"error_sending": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>There was an error"
|
||||
" uploading the video</b>"
|
||||
),
|
||||
}
|
||||
strings_ru = {
|
||||
"video_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка внутри"
|
||||
" модуля.</b>"
|
||||
),
|
||||
"processing": (
|
||||
"<b><emoji document_id=5334643333488713810>🌐</emoji>Обработка видео...</b>"
|
||||
),
|
||||
"sending": (
|
||||
"<b><emoji document_id=5371074117971745503>🤡</emoji>Отправка видео...</b>"
|
||||
),
|
||||
"error_downloading": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка при загрузке"
|
||||
" видео.</b>"
|
||||
),
|
||||
"error_sending": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка при отправке"
|
||||
" видео.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Сделать клавном <ник> или реплай")
|
||||
async def clown(self, message: Message):
|
||||
"""Добавляет текст поверх видео"""
|
||||
if not (
|
||||
username := "pov - "
|
||||
+ (
|
||||
await self._get_username(reply_message.sender_id)
|
||||
if (reply_message := await message.get_reply_message())
|
||||
else utils.get_args_raw(message)
|
||||
)
|
||||
):
|
||||
await utils.answer(message, self.strings("video_not_found"))
|
||||
return
|
||||
|
||||
video_url = "https://github.com/N3rcy/modules/raw/refs/heads/main/clown_asset.mp4"
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
video_path = os.path.join(temp_dir, "clown_video.mp4")
|
||||
output_path = os.path.join(temp_dir, "clown_output.mp4")
|
||||
|
||||
await utils.answer(message, self.strings("processing"))
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(requests.get, video_url)
|
||||
response.raise_for_status()
|
||||
with open(video_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_downloading"))
|
||||
return
|
||||
|
||||
command = f"ffmpeg -i {video_path} -vf \"drawtext=text='{username}':fontsize=50:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/4-150\" -c:a copy {output_path}"
|
||||
os.system(command)
|
||||
await utils.answer(message, self.strings("sending"))
|
||||
|
||||
try:
|
||||
await utils.answer_file(
|
||||
message,
|
||||
output_path,
|
||||
video_note=True,
|
||||
)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_sending"))
|
||||
|
||||
async def _get_username(self, user_id: int) -> str:
|
||||
user = await self.client.get_entity(user_id, exp=0)
|
||||
return user.username or user.first_name
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# scope: ffmpeg
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ClownModule(loader.Module):
|
||||
"""Модуль для клоунизации 'pov - <username>'"""
|
||||
|
||||
strings = {
|
||||
"name": "ClownMod",
|
||||
"video_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Error inside module."
|
||||
" (video_not_found)</b>"
|
||||
),
|
||||
"processing": (
|
||||
"<b><emoji document_id=5334643333488713810>🌐</emoji>Processing"
|
||||
" video...</b>"
|
||||
),
|
||||
"sending": (
|
||||
"<b><emoji document_id=5371074117971745503>🤡</emoji>Sending video...</b>"
|
||||
),
|
||||
"error_downloading": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Error inside module."
|
||||
" (error_downloading)</b>"
|
||||
),
|
||||
"error_sending": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>There was an error"
|
||||
" uploading the video</b>"
|
||||
),
|
||||
}
|
||||
strings_ru = {
|
||||
"video_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка внутри"
|
||||
" модуля.</b>"
|
||||
),
|
||||
"processing": (
|
||||
"<b><emoji document_id=5334643333488713810>🌐</emoji>Обработка видео...</b>"
|
||||
),
|
||||
"sending": (
|
||||
"<b><emoji document_id=5371074117971745503>🤡</emoji>Отправка видео...</b>"
|
||||
),
|
||||
"error_downloading": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка при загрузке"
|
||||
" видео.</b>"
|
||||
),
|
||||
"error_sending": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка при отправке"
|
||||
" видео.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Сделать клавном <ник> или реплай")
|
||||
async def clown(self, message: Message):
|
||||
"""Добавляет текст поверх видео"""
|
||||
if not (
|
||||
username := "pov - "
|
||||
+ (
|
||||
await self._get_username(reply_message.sender_id)
|
||||
if (reply_message := await message.get_reply_message())
|
||||
else utils.get_args_raw(message)
|
||||
)
|
||||
):
|
||||
await utils.answer(message, self.strings("video_not_found"))
|
||||
return
|
||||
|
||||
video_url = "https://github.com/N3rcy/modules/raw/refs/heads/main/clown_asset.mp4"
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
video_path = os.path.join(temp_dir, "clown_video.mp4")
|
||||
output_path = os.path.join(temp_dir, "clown_output.mp4")
|
||||
|
||||
await utils.answer(message, self.strings("processing"))
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(requests.get, video_url)
|
||||
response.raise_for_status()
|
||||
with open(video_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_downloading"))
|
||||
return
|
||||
|
||||
command = f"ffmpeg -i {video_path} -vf \"drawtext=text='{username}':fontsize=50:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/4-150\" -c:a copy {output_path}"
|
||||
os.system(command)
|
||||
await utils.answer(message, self.strings("sending"))
|
||||
|
||||
try:
|
||||
await utils.answer_file(
|
||||
message,
|
||||
output_path,
|
||||
video_note=True,
|
||||
)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("error_sending"))
|
||||
|
||||
async def _get_username(self, user_id: int) -> str:
|
||||
user = await self.client.get_entity(user_id, exp=0)
|
||||
return user.username or user.first_name
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# requires: bs4
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class EmojiInfo(loader.Module):
|
||||
"""Module for retrieving information about emojis from emojipedia.org"""
|
||||
|
||||
strings = {
|
||||
"name": "EmojiInfo",
|
||||
"emoji_not_found": "Emoji not found.",
|
||||
"error": "Error occurred while retrieving emoji information. Error: ",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"emoji_not_found": "Эмодзи не найдено.",
|
||||
"error": "Произошла ошибка при получении информации об эмодзи. Ошибка: ",
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Получить информацию об эмодзи")
|
||||
async def emoji(self, message: Message):
|
||||
"""Retrieve information about an emoji"""
|
||||
if not (emoji := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["emoji_not_found"])
|
||||
return
|
||||
|
||||
try:
|
||||
url = f"https://emojipedia.org/{emoji}/"
|
||||
response = await utils.run_sync(requests.get, url)
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.content, "html.parser")
|
||||
|
||||
emoji_name = soup.find('title').text
|
||||
emoji_description = soup.find('div', {'class': 'HtmlContent_html-content-container__Ow2Bk'}).text
|
||||
emoji_codepoints = ' '.join(['U+{:X}'.format(ord(char)) for char in emoji])
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
(
|
||||
f"<b>Name: {utils.escape_html(emoji_name)}</b>\n<b>Description:</b>"
|
||||
f" {utils.escape_html(emoji_description)}\n<b>Codepoints:</b>"
|
||||
f" {utils.escape_html(emoji_codepoints)}"
|
||||
),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
await utils.answer(
|
||||
message, self.strings("error") + utils.escape_html(str(e))
|
||||
)
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# requires: bs4
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class EmojiInfo(loader.Module):
|
||||
"""Module for retrieving information about emojis from emojipedia.org"""
|
||||
|
||||
strings = {
|
||||
"name": "EmojiInfo",
|
||||
"emoji_not_found": "Emoji not found.",
|
||||
"error": "Error occurred while retrieving emoji information. Error: ",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"emoji_not_found": "Эмодзи не найдено.",
|
||||
"error": "Произошла ошибка при получении информации об эмодзи. Ошибка: ",
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Получить информацию об эмодзи")
|
||||
async def emoji(self, message: Message):
|
||||
"""Retrieve information about an emoji"""
|
||||
if not (emoji := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["emoji_not_found"])
|
||||
return
|
||||
|
||||
try:
|
||||
url = f"https://emojipedia.org/{emoji}/"
|
||||
response = await utils.run_sync(requests.get, url)
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.content, "html.parser")
|
||||
|
||||
emoji_name = soup.find('title').text
|
||||
emoji_description = soup.find('div', {'class': 'HtmlContent_html-content-container__Ow2Bk'}).text
|
||||
emoji_codepoints = ' '.join(['U+{:X}'.format(ord(char)) for char in emoji])
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
(
|
||||
f"<b>Name: {utils.escape_html(emoji_name)}</b>\n<b>Description:</b>"
|
||||
f" {utils.escape_html(emoji_description)}\n<b>Codepoints:</b>"
|
||||
f" {utils.escape_html(emoji_codepoints)}"
|
||||
),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
await utils.answer(
|
||||
message, self.strings("error") + utils.escape_html(str(e))
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
watch
|
||||
twitch
|
||||
clown
|
||||
GameInfo
|
||||
news
|
||||
progmusic
|
||||
whisper
|
||||
github
|
||||
jikan
|
||||
ocr
|
||||
emoji
|
||||
top
|
||||
watch
|
||||
twitch
|
||||
clown
|
||||
GameInfo
|
||||
news
|
||||
progmusic
|
||||
whisper
|
||||
github
|
||||
jikan
|
||||
ocr
|
||||
emoji
|
||||
top
|
||||
|
||||
@@ -1,136 +1,136 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class GitHubMod(loader.Module):
|
||||
"""Module for fetching GitHub profile or repository information"""
|
||||
|
||||
strings = {
|
||||
"name": "GitHubMod",
|
||||
"profile_info": "<b>GitHub Profile Info: </b>",
|
||||
"repo_info": "<b>GitHub Repository Info: </b>",
|
||||
"invalid_link": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>Invalid GitHub link."
|
||||
" The correct link should start with https://github.com...</b>"
|
||||
),
|
||||
"user_not_found": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>User not found.</b>"
|
||||
),
|
||||
"repo_not_found": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>Repository not"
|
||||
" found.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(en_doc="<profile / url> - Fetch information about GitHub profile")
|
||||
async def gitprof(self, message: Message):
|
||||
"""<profile / url> - Fetch information about GitHub profile"""
|
||||
if not (link := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["invalid_link"])
|
||||
return
|
||||
|
||||
if link.startswith("https://github.com/"):
|
||||
username = link.split("/")[3]
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(
|
||||
requests.get, f"https://api.github.com/users/{username}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
user_data = response.json()
|
||||
info_text = (
|
||||
f"{self.strings['profile_info']}\n\n<b><emoji"
|
||||
" document_id=5224371968014299199>📦</emoji>Link:</b>"
|
||||
f" {link}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Username:</b>"
|
||||
f" {user_data.get('login', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Name:</b>"
|
||||
f" {user_data.get('name', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222030772751314651>🖌</emoji>Bio:</b>"
|
||||
f" {user_data.get('bio', 'N/A')}\n<b><emoji"
|
||||
" document_id=5221924764368515209>🪧</emoji>Location:</b>"
|
||||
f" {user_data.get('location', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222473609649337576>🔥</emoji>Followers:</b>"
|
||||
f" {user_data.get('followers', 'N/A')}\n<b><emoji"
|
||||
" document_id=5221962650275034448>❤️</emoji>Following:</b>"
|
||||
f" {user_data.get('following', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222341131383091841>📗</emoji>Public Repositories:</b>"
|
||||
f" {user_data.get('public_repos', 'N/A')}\n"
|
||||
)
|
||||
|
||||
if avatar_url := user_data.get("avatar_url"):
|
||||
await utils.answer_file(
|
||||
message,
|
||||
avatar_url,
|
||||
info_text,
|
||||
link_preview=False,
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, info_text)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings["user_not_found"])
|
||||
|
||||
@loader.command(ru_doc="Fetch information about GitHub repository")
|
||||
async def gitrepo(self, message: Message):
|
||||
"""Fetch information about GitHub repository"""
|
||||
if not (link := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["invalid_link"])
|
||||
return
|
||||
|
||||
if link.startswith("https://github.com/"):
|
||||
parts = link.split("/")
|
||||
if len(parts) >= 5:
|
||||
username = parts[3]
|
||||
repo_name = parts[4]
|
||||
elif len(link.split("/")) == 2:
|
||||
username, repo_name = link.split("/")
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(
|
||||
requests.get, f"https://api.github.com/repos/{username}/{repo_name}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
repo_data = response.json()
|
||||
info_text = (
|
||||
f"{self.strings['repo_info']}\n\n<b><emoji"
|
||||
" document_id=5224371968014299199>📦</emoji>Link:</b>"
|
||||
f" {link}\n<b><emoji"
|
||||
" document_id=5222341131383091841>📗</emoji>Repository:</b>"
|
||||
f" {repo_data.get('name', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222030772751314651>🖌</emoji>Description:</b>"
|
||||
f" {repo_data.get('description', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Language:</b>"
|
||||
f" {repo_data.get('language', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222473609649337576>🔥</emoji>Stars:</b>"
|
||||
f" {repo_data.get('stargazers_count', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222331261548246439>↕️</emoji>Forks:</b>"
|
||||
f" {repo_data.get('forks_count', 'N/A')}\n<b><emoji"
|
||||
" document_id=5334704798765686555>👀</emoji>Watchers:</b>"
|
||||
f" {repo_data.get('watchers_count', 'N/A')}\n"
|
||||
)
|
||||
if avatar_url := repo_data.get("avatar_url"):
|
||||
await utils.answer_file(
|
||||
message,
|
||||
avatar_url,
|
||||
info_text,
|
||||
link_preview=False,
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, info_text)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings["repo_not_found"])
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class GitHubMod(loader.Module):
|
||||
"""Module for fetching GitHub profile or repository information"""
|
||||
|
||||
strings = {
|
||||
"name": "GitHubMod",
|
||||
"profile_info": "<b>GitHub Profile Info: </b>",
|
||||
"repo_info": "<b>GitHub Repository Info: </b>",
|
||||
"invalid_link": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>Invalid GitHub link."
|
||||
" The correct link should start with https://github.com...</b>"
|
||||
),
|
||||
"user_not_found": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>User not found.</b>"
|
||||
),
|
||||
"repo_not_found": (
|
||||
"<b><emoji document_id=5978859389614821335>❌</emoji>Repository not"
|
||||
" found.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(en_doc="<profile / url> - Fetch information about GitHub profile")
|
||||
async def gitprof(self, message: Message):
|
||||
"""<profile / url> - Fetch information about GitHub profile"""
|
||||
if not (link := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["invalid_link"])
|
||||
return
|
||||
|
||||
if link.startswith("https://github.com/"):
|
||||
username = link.split("/")[3]
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(
|
||||
requests.get, f"https://api.github.com/users/{username}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
user_data = response.json()
|
||||
info_text = (
|
||||
f"{self.strings['profile_info']}\n\n<b><emoji"
|
||||
" document_id=5224371968014299199>📦</emoji>Link:</b>"
|
||||
f" {link}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Username:</b>"
|
||||
f" {user_data.get('login', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Name:</b>"
|
||||
f" {user_data.get('name', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222030772751314651>🖌</emoji>Bio:</b>"
|
||||
f" {user_data.get('bio', 'N/A')}\n<b><emoji"
|
||||
" document_id=5221924764368515209>🪧</emoji>Location:</b>"
|
||||
f" {user_data.get('location', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222473609649337576>🔥</emoji>Followers:</b>"
|
||||
f" {user_data.get('followers', 'N/A')}\n<b><emoji"
|
||||
" document_id=5221962650275034448>❤️</emoji>Following:</b>"
|
||||
f" {user_data.get('following', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222341131383091841>📗</emoji>Public Repositories:</b>"
|
||||
f" {user_data.get('public_repos', 'N/A')}\n"
|
||||
)
|
||||
|
||||
if avatar_url := user_data.get("avatar_url"):
|
||||
await utils.answer_file(
|
||||
message,
|
||||
avatar_url,
|
||||
info_text,
|
||||
link_preview=False,
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, info_text)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings["user_not_found"])
|
||||
|
||||
@loader.command(ru_doc="Fetch information about GitHub repository")
|
||||
async def gitrepo(self, message: Message):
|
||||
"""Fetch information about GitHub repository"""
|
||||
if not (link := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings["invalid_link"])
|
||||
return
|
||||
|
||||
if link.startswith("https://github.com/"):
|
||||
parts = link.split("/")
|
||||
if len(parts) >= 5:
|
||||
username = parts[3]
|
||||
repo_name = parts[4]
|
||||
elif len(link.split("/")) == 2:
|
||||
username, repo_name = link.split("/")
|
||||
|
||||
try:
|
||||
response = await utils.run_sync(
|
||||
requests.get, f"https://api.github.com/repos/{username}/{repo_name}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
repo_data = response.json()
|
||||
info_text = (
|
||||
f"{self.strings['repo_info']}\n\n<b><emoji"
|
||||
" document_id=5224371968014299199>📦</emoji>Link:</b>"
|
||||
f" {link}\n<b><emoji"
|
||||
" document_id=5222341131383091841>📗</emoji>Repository:</b>"
|
||||
f" {repo_data.get('name', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222030772751314651>🖌</emoji>Description:</b>"
|
||||
f" {repo_data.get('description', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222465715499446573>🌐</emoji>Language:</b>"
|
||||
f" {repo_data.get('language', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222473609649337576>🔥</emoji>Stars:</b>"
|
||||
f" {repo_data.get('stargazers_count', 'N/A')}\n<b><emoji"
|
||||
" document_id=5222331261548246439>↕️</emoji>Forks:</b>"
|
||||
f" {repo_data.get('forks_count', 'N/A')}\n<b><emoji"
|
||||
" document_id=5334704798765686555>👀</emoji>Watchers:</b>"
|
||||
f" {repo_data.get('watchers_count', 'N/A')}\n"
|
||||
)
|
||||
if avatar_url := repo_data.get("avatar_url"):
|
||||
await utils.answer_file(
|
||||
message,
|
||||
avatar_url,
|
||||
info_text,
|
||||
link_preview=False,
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, info_text)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings["repo_not_found"])
|
||||
|
||||
@@ -1,454 +1,454 @@
|
||||
__version__ = (1, 1, 0)
|
||||
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from deep_translator import GoogleTranslator
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
languages = ["ru", "en", "ja"]
|
||||
|
||||
|
||||
@loader.tds
|
||||
class JikanModule(loader.Module):
|
||||
"""Module for working with Jikan API"""
|
||||
|
||||
strings = {
|
||||
"name": "JikanModule",
|
||||
"anime_not_found": "<b>No anime found.</b>",
|
||||
"character_not_found": "<b>No character found.</b>",
|
||||
"manga_not_found": "<b>No manga found.</b>",
|
||||
"expression_missing": "<b>Please specify a search query.</b>",
|
||||
"result": "<b>Result:</b>\n{result}",
|
||||
"error": "<b>Error:</b> {error}",
|
||||
}
|
||||
strings_ru = {
|
||||
"anime_not_found": "<b>Аниме не найдено.</b>",
|
||||
"character_not_found": "<b>Персонаж не найден.</b>",
|
||||
"manga_not_found": "<b>Манга не найдена.</b>",
|
||||
"expression_missing": "<b>Пожалуйста, укажите поисковой запрос.</b>",
|
||||
"result": "<b>Результат:</b>\n{result}",
|
||||
"error": "<b>Ошибка:</b> {error}",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"language",
|
||||
"en",
|
||||
lambda: "Language of output",
|
||||
validator=loader.validators.Choice(languages),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Поиск аниме по названию",
|
||||
en_doc="Search for anime by title",
|
||||
)
|
||||
async def sanime(self, message: Message):
|
||||
"""Search for anime by title"""
|
||||
query = utils.get_args_raw(message)
|
||||
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
tquery = translator.translate(query)
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/anime",
|
||||
params={"q": tquery},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["anime_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"][0]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
start_date = anime.get("aired").get("from") if "aired" in anime else None
|
||||
end_date = anime.get("aired").get("to") if "aired" in anime else None
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
sfw = anime.get("sfw") is None
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if start_date:
|
||||
start_date = start_date.split("T")[0]
|
||||
result += f"<b>Start Date:</b> {start_date}\n"
|
||||
if end_date:
|
||||
end_date = end_date.split("T")[0]
|
||||
result += f"<b>End Date:</b> {end_date}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if sfw:
|
||||
result += f"<b>SFW:</b> Yes\n"
|
||||
else:
|
||||
result += f"<b>SFW:</b> No\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
else:
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(
|
||||
self.strings["result"].format(result=result)
|
||||
)
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Поиск манги по названию", en_doc="Search manga by title")
|
||||
async def smanga(self, m: Message):
|
||||
"""Search manga by title"""
|
||||
query = utils.get_args_raw(m)
|
||||
|
||||
if not query:
|
||||
await utils.answer(m, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/manga",
|
||||
params={"q": query},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(m, self.strings["manga_not_found"])
|
||||
return
|
||||
|
||||
manga = data["data"][0]
|
||||
|
||||
title = manga.get("title")
|
||||
title_english = manga.get("title_english")
|
||||
title_japanese = manga.get("title_japanese")
|
||||
type_ = manga.get("type")
|
||||
chapters = manga.get("chapters")
|
||||
volumes = manga.get("volumes")
|
||||
status = manga.get("status")
|
||||
start_date = (
|
||||
manga.get("published").get("from") if "published" in manga else None
|
||||
)
|
||||
end_date = manga.get("published").get("to") if "published" in manga else None
|
||||
score = manga.get("score")
|
||||
synopsis = manga.get("synopsis")
|
||||
sfw = manga.get("explicit_genres") is None
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if chapters:
|
||||
result += f"<b>Chapters:</b> {chapters}\n"
|
||||
if volumes:
|
||||
result += f"<b>Volumes:</b> {volumes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if start_date:
|
||||
start_date = start_date.split("T")[0]
|
||||
result += f"<b>Start Date:</b> {start_date}\n"
|
||||
if end_date:
|
||||
end_date = end_date.split("T")[0]
|
||||
result += f"<b>End Date:</b> {end_date}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if sfw:
|
||||
result += f"<b>SFW:</b> Yes\n"
|
||||
else:
|
||||
result += f"<b>SFW:</b> No\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(m, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(m, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Поиск персонажа по имени",
|
||||
en_doc="Search character by name",
|
||||
)
|
||||
async def scharacter(self, message: Message):
|
||||
"""Search character by name"""
|
||||
query = utils.get_args_raw(message)
|
||||
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/characters",
|
||||
params={"q": query},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["character_not_found"])
|
||||
return
|
||||
|
||||
character = data["data"][0]
|
||||
|
||||
name = character.get("name")
|
||||
name_kanji = character.get("name_kanji")
|
||||
nicknames = character.get("nicknames")
|
||||
favorites = character.get("favorites")
|
||||
about = character.get("about")
|
||||
|
||||
result = f"<b>Name:</b> {name}\n"
|
||||
if name_kanji:
|
||||
result += f"<b>Kanji Name:</b> {name_kanji}\n"
|
||||
if nicknames:
|
||||
result += f"<b>Nicknames:</b> {', '.join(nicknames)}\n"
|
||||
if favorites is not None:
|
||||
result += f"<b>Favorites:</b> {favorites}\n"
|
||||
if about:
|
||||
result += f"<b>About:</b> {about}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Получить рекомендации аниме",
|
||||
en_doc="Get anime recommendations",
|
||||
)
|
||||
async def rсanime(self, message: Message):
|
||||
"""Get anime recommendations"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/recommendations/anime",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["error"].format(error="No recommendations found."),
|
||||
)
|
||||
return
|
||||
|
||||
recommendations = data["data"][:3]
|
||||
|
||||
result = ""
|
||||
for recommendation in recommendations:
|
||||
title = recommendation["entry"][0].get("title")
|
||||
content = recommendation.get("content")
|
||||
user = recommendation.get("user")
|
||||
username = user.get("username") if user else None
|
||||
|
||||
result += f"<b>Title:</b> {title}\n" if title else ""
|
||||
result += f"<b>Content:</b> {content}\n" if content else ""
|
||||
result += f"<b>User:</b> {username}\n" if username else ""
|
||||
result += "\n"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Получить рекомендации манги",
|
||||
en_doc="Get manga recommendations",
|
||||
)
|
||||
async def rсmanga(self, message: Message):
|
||||
"""Get manga recommendations"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/recommendations/manga",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["error"].format(error="No recommendations found."),
|
||||
)
|
||||
return
|
||||
|
||||
recommendations = data["data"][:3]
|
||||
|
||||
result = ""
|
||||
for recommendation in recommendations:
|
||||
title = recommendation["entry"][0].get("title")
|
||||
content = recommendation.get("content")
|
||||
user = recommendation.get("user")
|
||||
username = user.get("username") if user else None
|
||||
|
||||
result += f"<b>Title:</b> {title}\n" if title else ""
|
||||
result += f"<b>Content:</b> {content}\n" if content else ""
|
||||
result += f"<b>User:</b> {username}\n" if username else ""
|
||||
result += "\n"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Случайное аниме", en_doc="Random anime")
|
||||
async def ranime(self, message: Message):
|
||||
"""Random anime"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/random/anime",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if not data.get("data"):
|
||||
await utils.answer(message, self.strings["anime_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
airing = anime.get("airing")
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if airing is not None:
|
||||
result += f"<b>Airing:</b> {airing}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Случайная манга", en_doc="Random manga")
|
||||
async def rmanga(self, message: Message):
|
||||
"""Random manga"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/random/manga",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["manga_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
airing = anime.get("airing")
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if airing is not None:
|
||||
result += f"<b>Airing:</b> {airing}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
__version__ = (1, 1, 0)
|
||||
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import requests
|
||||
from deep_translator import GoogleTranslator
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
languages = ["ru", "en", "ja"]
|
||||
|
||||
|
||||
@loader.tds
|
||||
class JikanModule(loader.Module):
|
||||
"""Module for working with Jikan API"""
|
||||
|
||||
strings = {
|
||||
"name": "JikanModule",
|
||||
"anime_not_found": "<b>No anime found.</b>",
|
||||
"character_not_found": "<b>No character found.</b>",
|
||||
"manga_not_found": "<b>No manga found.</b>",
|
||||
"expression_missing": "<b>Please specify a search query.</b>",
|
||||
"result": "<b>Result:</b>\n{result}",
|
||||
"error": "<b>Error:</b> {error}",
|
||||
}
|
||||
strings_ru = {
|
||||
"anime_not_found": "<b>Аниме не найдено.</b>",
|
||||
"character_not_found": "<b>Персонаж не найден.</b>",
|
||||
"manga_not_found": "<b>Манга не найдена.</b>",
|
||||
"expression_missing": "<b>Пожалуйста, укажите поисковой запрос.</b>",
|
||||
"result": "<b>Результат:</b>\n{result}",
|
||||
"error": "<b>Ошибка:</b> {error}",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"language",
|
||||
"en",
|
||||
lambda: "Language of output",
|
||||
validator=loader.validators.Choice(languages),
|
||||
),
|
||||
)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Поиск аниме по названию",
|
||||
en_doc="Search for anime by title",
|
||||
)
|
||||
async def sanime(self, message: Message):
|
||||
"""Search for anime by title"""
|
||||
query = utils.get_args_raw(message)
|
||||
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
tquery = translator.translate(query)
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/anime",
|
||||
params={"q": tquery},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["anime_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"][0]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
start_date = anime.get("aired").get("from") if "aired" in anime else None
|
||||
end_date = anime.get("aired").get("to") if "aired" in anime else None
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
sfw = anime.get("sfw") is None
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if start_date:
|
||||
start_date = start_date.split("T")[0]
|
||||
result += f"<b>Start Date:</b> {start_date}\n"
|
||||
if end_date:
|
||||
end_date = end_date.split("T")[0]
|
||||
result += f"<b>End Date:</b> {end_date}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if sfw:
|
||||
result += f"<b>SFW:</b> Yes\n"
|
||||
else:
|
||||
result += f"<b>SFW:</b> No\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
else:
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(
|
||||
self.strings["result"].format(result=result)
|
||||
)
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Поиск манги по названию", en_doc="Search manga by title")
|
||||
async def smanga(self, m: Message):
|
||||
"""Search manga by title"""
|
||||
query = utils.get_args_raw(m)
|
||||
|
||||
if not query:
|
||||
await utils.answer(m, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/manga",
|
||||
params={"q": query},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(m, self.strings["manga_not_found"])
|
||||
return
|
||||
|
||||
manga = data["data"][0]
|
||||
|
||||
title = manga.get("title")
|
||||
title_english = manga.get("title_english")
|
||||
title_japanese = manga.get("title_japanese")
|
||||
type_ = manga.get("type")
|
||||
chapters = manga.get("chapters")
|
||||
volumes = manga.get("volumes")
|
||||
status = manga.get("status")
|
||||
start_date = (
|
||||
manga.get("published").get("from") if "published" in manga else None
|
||||
)
|
||||
end_date = manga.get("published").get("to") if "published" in manga else None
|
||||
score = manga.get("score")
|
||||
synopsis = manga.get("synopsis")
|
||||
sfw = manga.get("explicit_genres") is None
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if chapters:
|
||||
result += f"<b>Chapters:</b> {chapters}\n"
|
||||
if volumes:
|
||||
result += f"<b>Volumes:</b> {volumes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if start_date:
|
||||
start_date = start_date.split("T")[0]
|
||||
result += f"<b>Start Date:</b> {start_date}\n"
|
||||
if end_date:
|
||||
end_date = end_date.split("T")[0]
|
||||
result += f"<b>End Date:</b> {end_date}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if sfw:
|
||||
result += f"<b>SFW:</b> Yes\n"
|
||||
else:
|
||||
result += f"<b>SFW:</b> No\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(m, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(m, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Поиск персонажа по имени",
|
||||
en_doc="Search character by name",
|
||||
)
|
||||
async def scharacter(self, message: Message):
|
||||
"""Search character by name"""
|
||||
query = utils.get_args_raw(message)
|
||||
|
||||
if not query:
|
||||
await utils.answer(message, self.strings["expression_missing"])
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target="en")
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/characters",
|
||||
params={"q": query},
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["character_not_found"])
|
||||
return
|
||||
|
||||
character = data["data"][0]
|
||||
|
||||
name = character.get("name")
|
||||
name_kanji = character.get("name_kanji")
|
||||
nicknames = character.get("nicknames")
|
||||
favorites = character.get("favorites")
|
||||
about = character.get("about")
|
||||
|
||||
result = f"<b>Name:</b> {name}\n"
|
||||
if name_kanji:
|
||||
result += f"<b>Kanji Name:</b> {name_kanji}\n"
|
||||
if nicknames:
|
||||
result += f"<b>Nicknames:</b> {', '.join(nicknames)}\n"
|
||||
if favorites is not None:
|
||||
result += f"<b>Favorites:</b> {favorites}\n"
|
||||
if about:
|
||||
result += f"<b>About:</b> {about}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Получить рекомендации аниме",
|
||||
en_doc="Get anime recommendations",
|
||||
)
|
||||
async def rсanime(self, message: Message):
|
||||
"""Get anime recommendations"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/recommendations/anime",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["error"].format(error="No recommendations found."),
|
||||
)
|
||||
return
|
||||
|
||||
recommendations = data["data"][:3]
|
||||
|
||||
result = ""
|
||||
for recommendation in recommendations:
|
||||
title = recommendation["entry"][0].get("title")
|
||||
content = recommendation.get("content")
|
||||
user = recommendation.get("user")
|
||||
username = user.get("username") if user else None
|
||||
|
||||
result += f"<b>Title:</b> {title}\n" if title else ""
|
||||
result += f"<b>Content:</b> {content}\n" if content else ""
|
||||
result += f"<b>User:</b> {username}\n" if username else ""
|
||||
result += "\n"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Получить рекомендации манги",
|
||||
en_doc="Get manga recommendations",
|
||||
)
|
||||
async def rсmanga(self, message: Message):
|
||||
"""Get manga recommendations"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/recommendations/manga",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings["error"].format(error="No recommendations found."),
|
||||
)
|
||||
return
|
||||
|
||||
recommendations = data["data"][:3]
|
||||
|
||||
result = ""
|
||||
for recommendation in recommendations:
|
||||
title = recommendation["entry"][0].get("title")
|
||||
content = recommendation.get("content")
|
||||
user = recommendation.get("user")
|
||||
username = user.get("username") if user else None
|
||||
|
||||
result += f"<b>Title:</b> {title}\n" if title else ""
|
||||
result += f"<b>Content:</b> {content}\n" if content else ""
|
||||
result += f"<b>User:</b> {username}\n" if username else ""
|
||||
result += "\n"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Случайное аниме", en_doc="Random anime")
|
||||
async def ranime(self, message: Message):
|
||||
"""Random anime"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/random/anime",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if not data.get("data"):
|
||||
await utils.answer(message, self.strings["anime_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
airing = anime.get("airing")
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if airing is not None:
|
||||
result += f"<b>Airing:</b> {airing}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@loader.command(ru_doc="Случайная манга", en_doc="Random manga")
|
||||
async def rmanga(self, message: Message):
|
||||
"""Random manga"""
|
||||
response = await utils.run_sync(
|
||||
requests.get,
|
||||
"https://api.jikan.moe/v4/random/manga",
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if "data" not in data or not data["data"]:
|
||||
await utils.answer(message, self.strings["manga_not_found"])
|
||||
return
|
||||
|
||||
anime = data["data"]
|
||||
|
||||
title = anime.get("title")
|
||||
title_english = anime.get("title_english")
|
||||
title_japanese = anime.get("title_japanese")
|
||||
type_ = anime.get("type")
|
||||
episodes = anime.get("episodes")
|
||||
status = anime.get("status")
|
||||
airing = anime.get("airing")
|
||||
duration = anime.get("duration")
|
||||
rating = anime.get("rating")
|
||||
score = anime.get("score")
|
||||
synopsis = anime.get("synopsis")
|
||||
|
||||
result = f"<b>Title:</b> {title}\n"
|
||||
if title_english:
|
||||
result += f"<b>English Title:</b> {title_english}\n"
|
||||
if title_japanese:
|
||||
result += f"<b>Japanese Title:</b> {title_japanese}\n"
|
||||
if type_:
|
||||
result += f"<b>Type:</b> {type_}\n"
|
||||
if episodes:
|
||||
result += f"<b>Episodes:</b> {episodes}\n"
|
||||
if status:
|
||||
result += f"<b>Status:</b> {status}\n"
|
||||
if airing is not None:
|
||||
result += f"<b>Airing:</b> {airing}\n"
|
||||
if duration:
|
||||
result += f"<b>Duration:</b> {duration}\n"
|
||||
if rating:
|
||||
result += f"<b>Rating:</b> {rating}\n"
|
||||
if score:
|
||||
result += f"<b>Score:</b> {score}\n"
|
||||
if synopsis:
|
||||
result += f"<b>Synopsis:</b> {synopsis}"
|
||||
|
||||
if self.config["language"] == "en":
|
||||
await utils.answer(message, self.strings["result"].format(result=result))
|
||||
return
|
||||
|
||||
translator = GoogleTranslator(source="auto", target=self.config["language"])
|
||||
translation = translator.translate(self.strings["result"].format(result=result))
|
||||
await utils.answer(message, translation)
|
||||
|
||||
@@ -1,96 +1,96 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# reqires: feedparser
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import feedparser
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
NEWS_SOURCES = {
|
||||
"Playground": "https://www.playground.ru/rss/news.xml",
|
||||
"BBC": "https://feeds.bbci.co.uk/news/world/rss.xml",
|
||||
"CNN": "http://rss.cnn.com/rss/edition.rss",
|
||||
"The Guardian": "https://www.theguardian.com/world/rss",
|
||||
"Le Monde": "https://www.lemonde.fr/rss/une.xml",
|
||||
"RIA": "https://ria.ru/export/rss2/archive/index.xml",
|
||||
"Lenta": "https://lenta.ru/rss/news",
|
||||
"RBC": "https://rssexport.rbc.ru/rbcnews/news/30/full.rss",
|
||||
}
|
||||
|
||||
|
||||
@loader.tds
|
||||
class NewsMod(loader.Module):
|
||||
"""Module for displaying news from various sources"""
|
||||
|
||||
strings = {"name": "NewsMod"}
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Playground")
|
||||
async def playground(self, m: Message):
|
||||
"""Get the latest news from Playground"""
|
||||
await self._get_news_from_source(m, "Playground")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с BBC")
|
||||
async def bbc(self, m: Message):
|
||||
"""Get the latest news from BBC"""
|
||||
await self._get_news_from_source(m, "BBC")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с CNN")
|
||||
async def cnn(self, m: Message):
|
||||
"""Get the latest news from CNN"""
|
||||
await self._get_news_from_source(m, "CNN")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с The Guardian")
|
||||
async def guardian(self, m: Message):
|
||||
"""Get the latest news from The Guardian"""
|
||||
await self._get_news_from_source(m, "The Guardian")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Le Monde")
|
||||
async def lemonde(self, m: Message):
|
||||
"""Get the latest news from Le Monde"""
|
||||
await self._get_news_from_source(m, "Le Monde")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Риа новости")
|
||||
async def ria(self, m: Message):
|
||||
"""Get the latest news from RIA"""
|
||||
await self._get_news_from_source(m, "RIA")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Рбк новости")
|
||||
async def rbc(self, m: Message):
|
||||
"""Get the latest news from rbc"""
|
||||
await self._get_news_from_source(m, "RBC")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Lenta")
|
||||
async def lenta(self, m: Message):
|
||||
"""Get the latest news from lenta"""
|
||||
await self._get_news_from_source(m, "Lenta")
|
||||
|
||||
async def _get_news_from_source(self, message: Message, source_name: str):
|
||||
"""Helper method to get news from a specific source"""
|
||||
if source_name not in NEWS_SOURCES:
|
||||
await utils.answer(
|
||||
message, f"Invalid news source: {utils.escape_html(source_name)}"
|
||||
)
|
||||
return
|
||||
|
||||
feed_url = NEWS_SOURCES[source_name]
|
||||
feed = feedparser.parse(feed_url)
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
"<b><emoji document_id=5433982607035474385>📰</emoji>Latest 15 news from"
|
||||
f" {source_name}</b>:\n\n"
|
||||
+ "\n\n".join(
|
||||
f"{i+1}: <a href='{entry.link}'>{utils.escape_html(entry.title)}</a>"
|
||||
for i, entry in enumerate(feed.entries[:15])
|
||||
),
|
||||
)
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# reqires: feedparser
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import feedparser
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
NEWS_SOURCES = {
|
||||
"Playground": "https://www.playground.ru/rss/news.xml",
|
||||
"BBC": "https://feeds.bbci.co.uk/news/world/rss.xml",
|
||||
"CNN": "http://rss.cnn.com/rss/edition.rss",
|
||||
"The Guardian": "https://www.theguardian.com/world/rss",
|
||||
"Le Monde": "https://www.lemonde.fr/rss/une.xml",
|
||||
"RIA": "https://ria.ru/export/rss2/archive/index.xml",
|
||||
"Lenta": "https://lenta.ru/rss/news",
|
||||
"RBC": "https://rssexport.rbc.ru/rbcnews/news/30/full.rss",
|
||||
}
|
||||
|
||||
|
||||
@loader.tds
|
||||
class NewsMod(loader.Module):
|
||||
"""Module for displaying news from various sources"""
|
||||
|
||||
strings = {"name": "NewsMod"}
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Playground")
|
||||
async def playground(self, m: Message):
|
||||
"""Get the latest news from Playground"""
|
||||
await self._get_news_from_source(m, "Playground")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с BBC")
|
||||
async def bbc(self, m: Message):
|
||||
"""Get the latest news from BBC"""
|
||||
await self._get_news_from_source(m, "BBC")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с CNN")
|
||||
async def cnn(self, m: Message):
|
||||
"""Get the latest news from CNN"""
|
||||
await self._get_news_from_source(m, "CNN")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с The Guardian")
|
||||
async def guardian(self, m: Message):
|
||||
"""Get the latest news from The Guardian"""
|
||||
await self._get_news_from_source(m, "The Guardian")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Le Monde")
|
||||
async def lemonde(self, m: Message):
|
||||
"""Get the latest news from Le Monde"""
|
||||
await self._get_news_from_source(m, "Le Monde")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Риа новости")
|
||||
async def ria(self, m: Message):
|
||||
"""Get the latest news from RIA"""
|
||||
await self._get_news_from_source(m, "RIA")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Рбк новости")
|
||||
async def rbc(self, m: Message):
|
||||
"""Get the latest news from rbc"""
|
||||
await self._get_news_from_source(m, "RBC")
|
||||
|
||||
@loader.command(ru_doc="Получить последние новости с Lenta")
|
||||
async def lenta(self, m: Message):
|
||||
"""Get the latest news from lenta"""
|
||||
await self._get_news_from_source(m, "Lenta")
|
||||
|
||||
async def _get_news_from_source(self, message: Message, source_name: str):
|
||||
"""Helper method to get news from a specific source"""
|
||||
if source_name not in NEWS_SOURCES:
|
||||
await utils.answer(
|
||||
message, f"Invalid news source: {utils.escape_html(source_name)}"
|
||||
)
|
||||
return
|
||||
|
||||
feed_url = NEWS_SOURCES[source_name]
|
||||
feed = feedparser.parse(feed_url)
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
"<b><emoji document_id=5433982607035474385>📰</emoji>Latest 15 news from"
|
||||
f" {source_name}</b>:\n\n"
|
||||
+ "\n\n".join(
|
||||
f"{i+1}: <a href='{entry.link}'>{utils.escape_html(entry.title)}</a>"
|
||||
for i, entry in enumerate(feed.entries[:15])
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,135 +1,135 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message, MessageMediaPhoto
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class OCRMod(loader.Module):
|
||||
"""Module for Optical Character Recognition"""
|
||||
|
||||
strings = {
|
||||
"name": "OCRMod",
|
||||
"file_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Not found to"
|
||||
" recognize, please reply.</b>"
|
||||
),
|
||||
"error": (
|
||||
f"<b><emoji document_id=5980953710157632545>❌</emoji>An error occurred"
|
||||
f" while processing the image. Error: </b>"
|
||||
),
|
||||
"text_result": (
|
||||
"<b><emoji document_id=6041850934756119589>🫠</emoji>Recognized:</b>\n"
|
||||
),
|
||||
"recognition": (
|
||||
"<b><emoji document_id=5307937750828194743>🫥</emoji>Recognition...</b>"
|
||||
),
|
||||
"no_api": "<b><emoji document_id=5980953710157632545>❌</emoji> Please insert api-key in config</b> (<code>.cfg ocrmod</code>)",
|
||||
"config_key": "Get key here: https://ocr.space/ocrapi/freekey",
|
||||
"language": ("🌐 Recognition language, available can be viewed here:"
|
||||
"https://ocr.space/OCRAPI#:~:text=faster%20upload%20speeds.-,language,-%5BOptional%5D%0AArabic"),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"file_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Не найдено, что"
|
||||
" распознавать, ответь реплаем.</b>"
|
||||
),
|
||||
"error": (
|
||||
f"<b><emoji document_id=5980953710157632545>❌</emoji>Произошла ошибка при"
|
||||
f" обработке изображения. Ошибка: </b>"
|
||||
),
|
||||
"text_result": (
|
||||
"<b><emoji document_id=6041850934756119589>🫠</emoji>Распознано:</b>\n"
|
||||
),
|
||||
"recognition": (
|
||||
"<b><emoji document_id=5307937750828194743>🫥</emoji>Распознаю...</b>"
|
||||
),
|
||||
"no_api": "<b><emoji document_id=5980953710157632545>❌</emoji> Пожалуйста, вставьте api-key в конфиг</b> (<code>.cfg ocrmod</code>)",
|
||||
"config_key": "Получить ключ можно здесь: https://ocr.space/ocrapi/freekey",
|
||||
"language": ("🌐 Язык распознавания, доступные можно посмотреть здесь:"
|
||||
"https://ocr.space/OCRAPI#:~:text=faster%20upload%20speeds.-,language,-%5BOptional%5D%0AArabic"),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
None,
|
||||
lambda: self.strings['config_key'],
|
||||
validator=loader.validators.Hidden(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"language",
|
||||
"eng",
|
||||
lambda: self.strings['language'],
|
||||
),
|
||||
)
|
||||
|
||||
async def ocr_space_file(self, filename, overlay=False):
|
||||
"""OCR.space API request with local file."""
|
||||
api_key = self.config["api_key"]
|
||||
language = self.config["language"]
|
||||
|
||||
payload = {
|
||||
"isOverlayRequired": overlay,
|
||||
"apikey": api_key,
|
||||
"language": language,
|
||||
}
|
||||
with open(filename, "rb") as f:
|
||||
r = await utils.run_sync(
|
||||
requests.post,
|
||||
"https://api.ocr.space/parse/image",
|
||||
files={filename: f},
|
||||
data=payload,
|
||||
)
|
||||
return r.content.decode()
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Распознать текст на фото из реплая",
|
||||
en_doc="Recognize text from an image in reply",
|
||||
)
|
||||
async def ocr(self, message: Message):
|
||||
"""Recognize text from an image in reply"""
|
||||
if not (reply_msg := await message.get_reply_message()) or not isinstance(
|
||||
reply_msg.media, MessageMediaPhoto
|
||||
):
|
||||
await utils.answer(message, self.strings("file_not_found"))
|
||||
return
|
||||
|
||||
if self.config['api_key'] == None:
|
||||
await utils.answer(message, self.strings['no_api'])
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("recognition"))
|
||||
filename = await reply_msg.download_media(file="image.png")
|
||||
result = await self.ocr_space_file(filename)
|
||||
os.remove(filename)
|
||||
|
||||
parsed_result = json.loads(result)
|
||||
parsed_text = parsed_result["ParsedResults"][0]["ParsedText"]
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
f"{self.strings('text_result')}\n{utils.escape_html(parsed_text)}",
|
||||
)
|
||||
except Exception as e:
|
||||
await utils.answer(
|
||||
message, self.strings("error") + utils.escape_html(str(e))
|
||||
)
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import requests
|
||||
from hikkatl.tl.types import Message, MessageMediaPhoto
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class OCRMod(loader.Module):
|
||||
"""Module for Optical Character Recognition"""
|
||||
|
||||
strings = {
|
||||
"name": "OCRMod",
|
||||
"file_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Not found to"
|
||||
" recognize, please reply.</b>"
|
||||
),
|
||||
"error": (
|
||||
f"<b><emoji document_id=5980953710157632545>❌</emoji>An error occurred"
|
||||
f" while processing the image. Error: </b>"
|
||||
),
|
||||
"text_result": (
|
||||
"<b><emoji document_id=6041850934756119589>🫠</emoji>Recognized:</b>\n"
|
||||
),
|
||||
"recognition": (
|
||||
"<b><emoji document_id=5307937750828194743>🫥</emoji>Recognition...</b>"
|
||||
),
|
||||
"no_api": "<b><emoji document_id=5980953710157632545>❌</emoji> Please insert api-key in config</b> (<code>.cfg ocrmod</code>)",
|
||||
"config_key": "Get key here: https://ocr.space/ocrapi/freekey",
|
||||
"language": ("🌐 Recognition language, available can be viewed here:"
|
||||
"https://ocr.space/OCRAPI#:~:text=faster%20upload%20speeds.-,language,-%5BOptional%5D%0AArabic"),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"file_not_found": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Не найдено, что"
|
||||
" распознавать, ответь реплаем.</b>"
|
||||
),
|
||||
"error": (
|
||||
f"<b><emoji document_id=5980953710157632545>❌</emoji>Произошла ошибка при"
|
||||
f" обработке изображения. Ошибка: </b>"
|
||||
),
|
||||
"text_result": (
|
||||
"<b><emoji document_id=6041850934756119589>🫠</emoji>Распознано:</b>\n"
|
||||
),
|
||||
"recognition": (
|
||||
"<b><emoji document_id=5307937750828194743>🫥</emoji>Распознаю...</b>"
|
||||
),
|
||||
"no_api": "<b><emoji document_id=5980953710157632545>❌</emoji> Пожалуйста, вставьте api-key в конфиг</b> (<code>.cfg ocrmod</code>)",
|
||||
"config_key": "Получить ключ можно здесь: https://ocr.space/ocrapi/freekey",
|
||||
"language": ("🌐 Язык распознавания, доступные можно посмотреть здесь:"
|
||||
"https://ocr.space/OCRAPI#:~:text=faster%20upload%20speeds.-,language,-%5BOptional%5D%0AArabic"),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
None,
|
||||
lambda: self.strings['config_key'],
|
||||
validator=loader.validators.Hidden(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"language",
|
||||
"eng",
|
||||
lambda: self.strings['language'],
|
||||
),
|
||||
)
|
||||
|
||||
async def ocr_space_file(self, filename, overlay=False):
|
||||
"""OCR.space API request with local file."""
|
||||
api_key = self.config["api_key"]
|
||||
language = self.config["language"]
|
||||
|
||||
payload = {
|
||||
"isOverlayRequired": overlay,
|
||||
"apikey": api_key,
|
||||
"language": language,
|
||||
}
|
||||
with open(filename, "rb") as f:
|
||||
r = await utils.run_sync(
|
||||
requests.post,
|
||||
"https://api.ocr.space/parse/image",
|
||||
files={filename: f},
|
||||
data=payload,
|
||||
)
|
||||
return r.content.decode()
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Распознать текст на фото из реплая",
|
||||
en_doc="Recognize text from an image in reply",
|
||||
)
|
||||
async def ocr(self, message: Message):
|
||||
"""Recognize text from an image in reply"""
|
||||
if not (reply_msg := await message.get_reply_message()) or not isinstance(
|
||||
reply_msg.media, MessageMediaPhoto
|
||||
):
|
||||
await utils.answer(message, self.strings("file_not_found"))
|
||||
return
|
||||
|
||||
if self.config['api_key'] == None:
|
||||
await utils.answer(message, self.strings['no_api'])
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("recognition"))
|
||||
filename = await reply_msg.download_media(file="image.png")
|
||||
result = await self.ocr_space_file(filename)
|
||||
os.remove(filename)
|
||||
|
||||
parsed_result = json.loads(result)
|
||||
parsed_text = parsed_result["ParsedResults"][0]["ParsedText"]
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
f"{self.strings('text_result')}\n{utils.escape_html(parsed_text)}",
|
||||
)
|
||||
except Exception as e:
|
||||
await utils.answer(
|
||||
message, self.strings("error") + utils.escape_html(str(e))
|
||||
)
|
||||
|
||||
@@ -1,120 +1,120 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import os
|
||||
import random
|
||||
|
||||
import aiohttp
|
||||
import feedparser
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
from ..inline.types import InlineCall
|
||||
|
||||
|
||||
@loader.tds
|
||||
class MusicModule(loader.Module):
|
||||
"""Module for music for programming from https://musicforprogramming.net/"""
|
||||
|
||||
strings = {
|
||||
"name": "ProgMusic",
|
||||
"fetching": "<b>Fetching music...</b>",
|
||||
"downloading": (
|
||||
"<b>Downloading... Please wait, usually it takes in 3-7 minutes ❤️</b>"
|
||||
),
|
||||
"download_failed": "<b>Failed to download music.</b>",
|
||||
"successful": "<b>Enjoy the music!</b>",
|
||||
}
|
||||
|
||||
@loader.command(en_doc="send random chill music")
|
||||
async def prmusic(self, message: Message):
|
||||
"""Send music for programming"""
|
||||
await message.edit(self.strings["fetching"])
|
||||
|
||||
rss_url = "https://musicforprogramming.net/rss.xml"
|
||||
feed = feedparser.parse(rss_url)
|
||||
|
||||
if not feed.entries:
|
||||
await message.edit("Failed to fetch music.")
|
||||
return
|
||||
|
||||
random_entry = random.choice(feed.entries)
|
||||
|
||||
title = random_entry.title
|
||||
description = random_entry.description
|
||||
link = random_entry.link
|
||||
download_url = random_entry.enclosures[0].href
|
||||
|
||||
if not download_url:
|
||||
await utils.answer(message, "Failed to fetch music.")
|
||||
return
|
||||
|
||||
await self.inline.form(
|
||||
text=f"<b>{title}</b>\n\n<b>Description:</b> {description}",
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[{"text": "Link 🔗", "url": link}],
|
||||
[
|
||||
{
|
||||
"text": "Download ⬇️",
|
||||
"callback": lambda c, u=download_url, l=link, t=title: self.download_and_send_music(
|
||||
c, message.chat_id, u, t, l
|
||||
),
|
||||
}
|
||||
],
|
||||
[{"text": "Close ❌", "callback": self.close}],
|
||||
],
|
||||
silent=True,
|
||||
)
|
||||
|
||||
async def close(self, call):
|
||||
"""Close the inline form"""
|
||||
await call.delete()
|
||||
|
||||
async def download_and_send_music(
|
||||
self,
|
||||
call: InlineCall,
|
||||
chat_id: int,
|
||||
download_url: str,
|
||||
title: str,
|
||||
link: str,
|
||||
):
|
||||
await call.edit(
|
||||
text=f"<b>Title: {title}</b>\n<b>Link: {link}</b>\n\n"
|
||||
+ self.strings("downloading")
|
||||
)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(download_url) as response:
|
||||
if response.status != 200:
|
||||
return
|
||||
|
||||
file = await response.read()
|
||||
|
||||
extension = download_url.split(".")[-1]
|
||||
|
||||
temp_file_path = os.path.join(os.getcwd(), f"chill.{extension}")
|
||||
with open(temp_file_path, "wb") as temp_file:
|
||||
temp_file.write(file)
|
||||
|
||||
await self.client.send_file(
|
||||
chat_id,
|
||||
temp_file_path,
|
||||
voice=True,
|
||||
only_document=False,
|
||||
caption=self.strings("successful")
|
||||
+ f"\n\n<b>Title: {utils.escape_html(title)}</b>\n<b>Link:"
|
||||
f" {utils.escape_html(link)}</b>",
|
||||
)
|
||||
|
||||
os.remove(temp_file_path)
|
||||
await call.delete()
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
import os
|
||||
import random
|
||||
|
||||
import aiohttp
|
||||
import feedparser
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
from ..inline.types import InlineCall
|
||||
|
||||
|
||||
@loader.tds
|
||||
class MusicModule(loader.Module):
|
||||
"""Module for music for programming from https://musicforprogramming.net/"""
|
||||
|
||||
strings = {
|
||||
"name": "ProgMusic",
|
||||
"fetching": "<b>Fetching music...</b>",
|
||||
"downloading": (
|
||||
"<b>Downloading... Please wait, usually it takes in 3-7 minutes ❤️</b>"
|
||||
),
|
||||
"download_failed": "<b>Failed to download music.</b>",
|
||||
"successful": "<b>Enjoy the music!</b>",
|
||||
}
|
||||
|
||||
@loader.command(en_doc="send random chill music")
|
||||
async def prmusic(self, message: Message):
|
||||
"""Send music for programming"""
|
||||
await message.edit(self.strings["fetching"])
|
||||
|
||||
rss_url = "https://musicforprogramming.net/rss.xml"
|
||||
feed = feedparser.parse(rss_url)
|
||||
|
||||
if not feed.entries:
|
||||
await message.edit("Failed to fetch music.")
|
||||
return
|
||||
|
||||
random_entry = random.choice(feed.entries)
|
||||
|
||||
title = random_entry.title
|
||||
description = random_entry.description
|
||||
link = random_entry.link
|
||||
download_url = random_entry.enclosures[0].href
|
||||
|
||||
if not download_url:
|
||||
await utils.answer(message, "Failed to fetch music.")
|
||||
return
|
||||
|
||||
await self.inline.form(
|
||||
text=f"<b>{title}</b>\n\n<b>Description:</b> {description}",
|
||||
message=message,
|
||||
reply_markup=[
|
||||
[{"text": "Link 🔗", "url": link}],
|
||||
[
|
||||
{
|
||||
"text": "Download ⬇️",
|
||||
"callback": lambda c, u=download_url, l=link, t=title: self.download_and_send_music(
|
||||
c, message.chat_id, u, t, l
|
||||
),
|
||||
}
|
||||
],
|
||||
[{"text": "Close ❌", "callback": self.close}],
|
||||
],
|
||||
silent=True,
|
||||
)
|
||||
|
||||
async def close(self, call):
|
||||
"""Close the inline form"""
|
||||
await call.delete()
|
||||
|
||||
async def download_and_send_music(
|
||||
self,
|
||||
call: InlineCall,
|
||||
chat_id: int,
|
||||
download_url: str,
|
||||
title: str,
|
||||
link: str,
|
||||
):
|
||||
await call.edit(
|
||||
text=f"<b>Title: {title}</b>\n<b>Link: {link}</b>\n\n"
|
||||
+ self.strings("downloading")
|
||||
)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(download_url) as response:
|
||||
if response.status != 200:
|
||||
return
|
||||
|
||||
file = await response.read()
|
||||
|
||||
extension = download_url.split(".")[-1]
|
||||
|
||||
temp_file_path = os.path.join(os.getcwd(), f"chill.{extension}")
|
||||
with open(temp_file_path, "wb") as temp_file:
|
||||
temp_file.write(file)
|
||||
|
||||
await self.client.send_file(
|
||||
chat_id,
|
||||
temp_file_path,
|
||||
voice=True,
|
||||
only_document=False,
|
||||
caption=self.strings("successful")
|
||||
+ f"\n\n<b>Title: {utils.escape_html(title)}</b>\n<b>Link:"
|
||||
f" {utils.escape_html(link)}</b>",
|
||||
)
|
||||
|
||||
os.remove(temp_file_path)
|
||||
await call.delete()
|
||||
|
||||
@@ -1,218 +1,218 @@
|
||||
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# requires: matplotlib
|
||||
|
||||
from hikkatl.types import Message, PeerUser, PeerChat, PeerChannel
|
||||
from .. import loader, utils
|
||||
|
||||
from collections import defaultdict
|
||||
import matplotlib.pyplot as plt
|
||||
import io
|
||||
import asyncio
|
||||
import warnings
|
||||
import numpy as np
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
from telethon.tl.functions.messages import SearchRequest, GetHistoryRequest
|
||||
from telethon.tl.types import InputMessagesFilterEmpty
|
||||
|
||||
plt.style.use('dark_background')
|
||||
|
||||
@loader.tds
|
||||
class Top(loader.Module):
|
||||
"""Module for viewing the top list in chat"""
|
||||
strings = {
|
||||
"name": "Top",
|
||||
"top": "Top users by message count",
|
||||
"topchat": "<emoji document_id=5323538339062628165>💬</emoji><b>Top users in</b>",
|
||||
"msgcount": "Message count",
|
||||
"loading": "<emoji document_id=5780543148782522693>🕒</emoji><b>Message counting has started, please wait, it may take a long time if there are a lot of messages in the chat</b>",
|
||||
"private_chat": "<emoji document_id=5323538339062628165>💬</emoji><b>Message count in private chat with</b>"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"top": "Топ пользователей по количеству сообщений",
|
||||
"topchat": "<emoji document_id=5323538339062628165>💬</emoji><b>Топ пользователей в</b>",
|
||||
"msgcount": "Количество сообщений",
|
||||
"loading": "<emoji document_id=5780543148782522693>🕒</emoji><b>Подсчет сообщений начался, пожалуйста подождите, это может занять много времени если в чате много сообщений</b>",
|
||||
"private_chat": "<emoji document_id=5323538339062628165>💬</emoji><b>Количество сообщений в личном чате с</b>"
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Посмотреть топ в чате")
|
||||
async def top(self, m: Message):
|
||||
"""View top in the chat"""
|
||||
await utils.answer(m, self.strings['loading'])
|
||||
|
||||
client = self.client
|
||||
|
||||
if isinstance(m.peer_id, PeerUser):
|
||||
chat_type = 'private'
|
||||
chat_id = m.peer_id.user_id
|
||||
elif isinstance(m.peer_id, PeerChat) or isinstance(m.peer_id, PeerChannel):
|
||||
chat_type = 'chat'
|
||||
chat_id = m.chat.id
|
||||
else:
|
||||
await utils.answer(m, "Unsupported chat type.")
|
||||
return
|
||||
|
||||
if chat_type == 'chat':
|
||||
users = await client.get_participants(chat_id)
|
||||
users_dict = {user.id: (user.username or user.first_name) for user in users}
|
||||
message_count = defaultdict(int)
|
||||
|
||||
for user_id in users_dict:
|
||||
result = await client(SearchRequest(
|
||||
peer=chat_id,
|
||||
q='',
|
||||
filter=InputMessagesFilterEmpty(),
|
||||
from_id=user_id,
|
||||
limit=0,
|
||||
min_date=None,
|
||||
max_date=None,
|
||||
offset_id=0,
|
||||
add_offset=0,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0
|
||||
))
|
||||
message_count[user_id] = result.count
|
||||
|
||||
sorted_message_count = sorted(message_count.items(), key=lambda item: item[1], reverse=True)
|
||||
top_users = sorted_message_count[:20]
|
||||
usernames = [users_dict[user_id] or "Unknown" for user_id, _ in top_users]
|
||||
counts = [count for _, count in top_users]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
|
||||
colors = self._generate_gradient('#8A2BE2', '#4B0082', len(usernames))
|
||||
bars = ax.barh(usernames, counts, color=colors, edgecolor='black', linewidth=0.5)
|
||||
|
||||
for bar in bars:
|
||||
bar.set_alpha(0.8)
|
||||
bar.set_hatch('///')
|
||||
|
||||
ax.set_xlabel(self.strings['msgcount'], fontsize=12, color='white')
|
||||
ax.set_title(self.strings['top'], fontsize=14, color='white', pad=20)
|
||||
ax.invert_yaxis()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_color('#8A2BE2')
|
||||
ax.spines['bottom'].set_color('#8A2BE2')
|
||||
|
||||
ax.grid(True, linestyle='--', alpha=0.6, color='gray')
|
||||
|
||||
for i, (bar, username) in enumerate(zip(bars, usernames)):
|
||||
if i < 3:
|
||||
bar.set_color('#FFD700')
|
||||
ax.text(bar.get_width() + 5, bar.get_y() + bar.get_height() / 2,
|
||||
f'#{i+1}', va='center', ha='left', color='#FFD700', fontsize=12)
|
||||
|
||||
buf = io.BytesIO()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore")
|
||||
plt.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
||||
buf.seek(0)
|
||||
|
||||
caption = f"{self.strings['topchat']} <b>{m.chat.title}:</b>\n"
|
||||
caption += "\n".join([f"{i+1}. {user} - {count}" for i, (user, count) in enumerate(zip(usernames, counts))])
|
||||
|
||||
await utils.answer_file(m, buf, caption, force_document=False)
|
||||
|
||||
else:
|
||||
me = await client.get_me()
|
||||
target = await client.get_entity(chat_id)
|
||||
|
||||
my_count, their_count = await asyncio.gather(
|
||||
self._get_message_count_fast(client, chat_id, me.id),
|
||||
self._get_message_count_fast(client, chat_id, target.id)
|
||||
)
|
||||
|
||||
message_counts = [(me.first_name, my_count), (target.first_name, their_count)]
|
||||
sorted_message_counts = sorted(message_counts, key=lambda item: item[1], reverse=True)
|
||||
|
||||
usernames = [user for user, _ in sorted_message_counts]
|
||||
counts = [count for _, count in sorted_message_counts]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
|
||||
colors = self._generate_gradient('#8A2BE2', '#4B0082', len(usernames))
|
||||
bars = ax.barh(usernames, counts, color=colors, edgecolor='black', linewidth=0.5)
|
||||
|
||||
for bar in bars:
|
||||
bar.set_alpha(0.8)
|
||||
bar.set_hatch('///')
|
||||
|
||||
ax.set_xlabel(self.strings['msgcount'], fontsize=12, color='white')
|
||||
ax.set_title(self.strings['top'], fontsize=14, color='white', pad=20)
|
||||
ax.invert_yaxis()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_color('#8A2BE2')
|
||||
ax.spines['bottom'].set_color('#8A2BE2')
|
||||
|
||||
ax.grid(True, linestyle='--', alpha=0.6, color='gray')
|
||||
|
||||
for i, (bar, username) in enumerate(zip(bars, usernames)):
|
||||
if i < 3:
|
||||
bar.set_color('#FFD700')
|
||||
ax.text(bar.get_width() + 5, bar.get_y() + bar.get_height() / 2,
|
||||
f'#{i+1}', va='center', ha='left', color='#FFD700', fontsize=12)
|
||||
|
||||
buf = io.BytesIO()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore")
|
||||
plt.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
||||
buf.seek(0)
|
||||
|
||||
caption = f"{self.strings['private_chat']} <b>{target.first_name}:</b>\n"
|
||||
caption += "\n".join([f'"{user}" - {count}' for user, count in zip(usernames, counts)])
|
||||
|
||||
await utils.answer_file(m, buf, caption, force_document=False)
|
||||
|
||||
async def _get_message_count_fast(self, client, chat_id, user_id):
|
||||
"""Получает количество сообщений от конкретного пользователя с использованием GetHistoryRequest"""
|
||||
total_count = 0
|
||||
offset_id = 0
|
||||
limit = 100
|
||||
|
||||
while True:
|
||||
history = await client(GetHistoryRequest(
|
||||
peer=chat_id,
|
||||
offset_id=offset_id,
|
||||
offset_date=None,
|
||||
add_offset=0,
|
||||
limit=limit,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0
|
||||
))
|
||||
|
||||
if not history.messages:
|
||||
break
|
||||
|
||||
for message in history.messages:
|
||||
if message.sender_id == user_id:
|
||||
total_count += 1
|
||||
|
||||
offset_id = history.messages[-1].id
|
||||
|
||||
if len(history.messages) < limit:
|
||||
break
|
||||
|
||||
return total_count
|
||||
|
||||
def _generate_gradient(self, start_color, end_color, n):
|
||||
"""Генерация градиента между двумя цветами"""
|
||||
cmap = LinearSegmentedColormap.from_list('custom_gradient', [start_color, end_color], N=n)
|
||||
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# requires: matplotlib
|
||||
|
||||
from hikkatl.types import Message, PeerUser, PeerChat, PeerChannel
|
||||
from .. import loader, utils
|
||||
|
||||
from collections import defaultdict
|
||||
import matplotlib.pyplot as plt
|
||||
import io
|
||||
import asyncio
|
||||
import warnings
|
||||
import numpy as np
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
from telethon.tl.functions.messages import SearchRequest, GetHistoryRequest
|
||||
from telethon.tl.types import InputMessagesFilterEmpty
|
||||
|
||||
plt.style.use('dark_background')
|
||||
|
||||
@loader.tds
|
||||
class Top(loader.Module):
|
||||
"""Module for viewing the top list in chat"""
|
||||
strings = {
|
||||
"name": "Top",
|
||||
"top": "Top users by message count",
|
||||
"topchat": "<emoji document_id=5323538339062628165>💬</emoji><b>Top users in</b>",
|
||||
"msgcount": "Message count",
|
||||
"loading": "<emoji document_id=5780543148782522693>🕒</emoji><b>Message counting has started, please wait, it may take a long time if there are a lot of messages in the chat</b>",
|
||||
"private_chat": "<emoji document_id=5323538339062628165>💬</emoji><b>Message count in private chat with</b>"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"top": "Топ пользователей по количеству сообщений",
|
||||
"topchat": "<emoji document_id=5323538339062628165>💬</emoji><b>Топ пользователей в</b>",
|
||||
"msgcount": "Количество сообщений",
|
||||
"loading": "<emoji document_id=5780543148782522693>🕒</emoji><b>Подсчет сообщений начался, пожалуйста подождите, это может занять много времени если в чате много сообщений</b>",
|
||||
"private_chat": "<emoji document_id=5323538339062628165>💬</emoji><b>Количество сообщений в личном чате с</b>"
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Посмотреть топ в чате")
|
||||
async def top(self, m: Message):
|
||||
"""View top in the chat"""
|
||||
await utils.answer(m, self.strings['loading'])
|
||||
|
||||
client = self.client
|
||||
|
||||
if isinstance(m.peer_id, PeerUser):
|
||||
chat_type = 'private'
|
||||
chat_id = m.peer_id.user_id
|
||||
elif isinstance(m.peer_id, PeerChat) or isinstance(m.peer_id, PeerChannel):
|
||||
chat_type = 'chat'
|
||||
chat_id = m.chat.id
|
||||
else:
|
||||
await utils.answer(m, "Unsupported chat type.")
|
||||
return
|
||||
|
||||
if chat_type == 'chat':
|
||||
users = await client.get_participants(chat_id)
|
||||
users_dict = {user.id: (user.username or user.first_name) for user in users}
|
||||
message_count = defaultdict(int)
|
||||
|
||||
for user_id in users_dict:
|
||||
result = await client(SearchRequest(
|
||||
peer=chat_id,
|
||||
q='',
|
||||
filter=InputMessagesFilterEmpty(),
|
||||
from_id=user_id,
|
||||
limit=0,
|
||||
min_date=None,
|
||||
max_date=None,
|
||||
offset_id=0,
|
||||
add_offset=0,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0
|
||||
))
|
||||
message_count[user_id] = result.count
|
||||
|
||||
sorted_message_count = sorted(message_count.items(), key=lambda item: item[1], reverse=True)
|
||||
top_users = sorted_message_count[:20]
|
||||
usernames = [users_dict[user_id] or "Unknown" for user_id, _ in top_users]
|
||||
counts = [count for _, count in top_users]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
|
||||
colors = self._generate_gradient('#8A2BE2', '#4B0082', len(usernames))
|
||||
bars = ax.barh(usernames, counts, color=colors, edgecolor='black', linewidth=0.5)
|
||||
|
||||
for bar in bars:
|
||||
bar.set_alpha(0.8)
|
||||
bar.set_hatch('///')
|
||||
|
||||
ax.set_xlabel(self.strings['msgcount'], fontsize=12, color='white')
|
||||
ax.set_title(self.strings['top'], fontsize=14, color='white', pad=20)
|
||||
ax.invert_yaxis()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_color('#8A2BE2')
|
||||
ax.spines['bottom'].set_color('#8A2BE2')
|
||||
|
||||
ax.grid(True, linestyle='--', alpha=0.6, color='gray')
|
||||
|
||||
for i, (bar, username) in enumerate(zip(bars, usernames)):
|
||||
if i < 3:
|
||||
bar.set_color('#FFD700')
|
||||
ax.text(bar.get_width() + 5, bar.get_y() + bar.get_height() / 2,
|
||||
f'#{i+1}', va='center', ha='left', color='#FFD700', fontsize=12)
|
||||
|
||||
buf = io.BytesIO()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore")
|
||||
plt.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
||||
buf.seek(0)
|
||||
|
||||
caption = f"{self.strings['topchat']} <b>{m.chat.title}:</b>\n"
|
||||
caption += "\n".join([f"{i+1}. {user} - {count}" for i, (user, count) in enumerate(zip(usernames, counts))])
|
||||
|
||||
await utils.answer_file(m, buf, caption, force_document=False)
|
||||
|
||||
else:
|
||||
me = await client.get_me()
|
||||
target = await client.get_entity(chat_id)
|
||||
|
||||
my_count, their_count = await asyncio.gather(
|
||||
self._get_message_count_fast(client, chat_id, me.id),
|
||||
self._get_message_count_fast(client, chat_id, target.id)
|
||||
)
|
||||
|
||||
message_counts = [(me.first_name, my_count), (target.first_name, their_count)]
|
||||
sorted_message_counts = sorted(message_counts, key=lambda item: item[1], reverse=True)
|
||||
|
||||
usernames = [user for user, _ in sorted_message_counts]
|
||||
counts = [count for _, count in sorted_message_counts]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
|
||||
colors = self._generate_gradient('#8A2BE2', '#4B0082', len(usernames))
|
||||
bars = ax.barh(usernames, counts, color=colors, edgecolor='black', linewidth=0.5)
|
||||
|
||||
for bar in bars:
|
||||
bar.set_alpha(0.8)
|
||||
bar.set_hatch('///')
|
||||
|
||||
ax.set_xlabel(self.strings['msgcount'], fontsize=12, color='white')
|
||||
ax.set_title(self.strings['top'], fontsize=14, color='white', pad=20)
|
||||
ax.invert_yaxis()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_color('#8A2BE2')
|
||||
ax.spines['bottom'].set_color('#8A2BE2')
|
||||
|
||||
ax.grid(True, linestyle='--', alpha=0.6, color='gray')
|
||||
|
||||
for i, (bar, username) in enumerate(zip(bars, usernames)):
|
||||
if i < 3:
|
||||
bar.set_color('#FFD700')
|
||||
ax.text(bar.get_width() + 5, bar.get_y() + bar.get_height() / 2,
|
||||
f'#{i+1}', va='center', ha='left', color='#FFD700', fontsize=12)
|
||||
|
||||
buf = io.BytesIO()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore")
|
||||
plt.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
||||
buf.seek(0)
|
||||
|
||||
caption = f"{self.strings['private_chat']} <b>{target.first_name}:</b>\n"
|
||||
caption += "\n".join([f'"{user}" - {count}' for user, count in zip(usernames, counts)])
|
||||
|
||||
await utils.answer_file(m, buf, caption, force_document=False)
|
||||
|
||||
async def _get_message_count_fast(self, client, chat_id, user_id):
|
||||
"""Получает количество сообщений от конкретного пользователя с использованием GetHistoryRequest"""
|
||||
total_count = 0
|
||||
offset_id = 0
|
||||
limit = 100
|
||||
|
||||
while True:
|
||||
history = await client(GetHistoryRequest(
|
||||
peer=chat_id,
|
||||
offset_id=offset_id,
|
||||
offset_date=None,
|
||||
add_offset=0,
|
||||
limit=limit,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0
|
||||
))
|
||||
|
||||
if not history.messages:
|
||||
break
|
||||
|
||||
for message in history.messages:
|
||||
if message.sender_id == user_id:
|
||||
total_count += 1
|
||||
|
||||
offset_id = history.messages[-1].id
|
||||
|
||||
if len(history.messages) < limit:
|
||||
break
|
||||
|
||||
return total_count
|
||||
|
||||
def _generate_gradient(self, start_color, end_color, n):
|
||||
"""Генерация градиента между двумя цветами"""
|
||||
cmap = LinearSegmentedColormap.from_list('custom_gradient', [start_color, end_color], N=n)
|
||||
return [cmap(i) for i in np.linspace(0, 1, n)]
|
||||
@@ -1,121 +1,121 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# reqires: youtube_dl
|
||||
|
||||
import youtube_dl
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TwitchModule(loader.Module):
|
||||
"""Module for downloading Twitch clips and videos"""
|
||||
|
||||
strings = {
|
||||
"name": "Twitchdl",
|
||||
"clip_download_started": (
|
||||
"<emoji document_id=5307773751796964107>⏳</emoji><b>Clip download started,"
|
||||
" please wait.</b>"
|
||||
),
|
||||
"clip_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Clip download"
|
||||
" completed. Sending...</b>"
|
||||
),
|
||||
"clip_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Clip download failed."
|
||||
" Ensure the correctness of the link.</b>"
|
||||
),
|
||||
"video_download_started": (
|
||||
"<b><emoji document_id=5307773751796964107>⏳</emoji>Video download"
|
||||
" started, please wait.</b>"
|
||||
),
|
||||
"video_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Video download"
|
||||
" completed. Sending...</b>"
|
||||
),
|
||||
"video_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Video download failed."
|
||||
" Ensure the correctness of the link.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"clip_download_started": (
|
||||
"<emoji document_id=5307773751796964107>⏳</emoji><b>Скачивание клипа"
|
||||
" началось, подождите.</b>"
|
||||
),
|
||||
"clip_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Скачивание клипа"
|
||||
" завершено. Отправляю...</b>"
|
||||
),
|
||||
"clip_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка скачивания"
|
||||
" клипа. Убедитесь в ссылке.</b>"
|
||||
),
|
||||
"video_download_started": (
|
||||
"<b><emoji document_id=5307773751796964107>⏳</emoji>Скачивание видео"
|
||||
" началось, подождите.</b>"
|
||||
),
|
||||
"video_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Скачивание видео"
|
||||
" завершено. Отправляю...</b>"
|
||||
),
|
||||
"video_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка скачивания"
|
||||
" видео. Убедитесь в ссылке.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Скачать клип с Twitch")
|
||||
async def twitch(self, message: Message):
|
||||
"""Download a clip from Twitch"""
|
||||
if not (clip_url := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("clip_download_failed"))
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("clip_download_started"))
|
||||
|
||||
with youtube_dl.YoutubeDL(
|
||||
{
|
||||
"outtmpl": "clip.mp4",
|
||||
"format": "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
}
|
||||
) as ydl:
|
||||
ydl.download([clip_url])
|
||||
|
||||
await utils.answer_file(message, "clip.mp4")
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("clip_download_failed"))
|
||||
|
||||
@loader.command(ru_doc="Скачать видео с Twitch")
|
||||
async def twitchvideo(self, message: Message):
|
||||
"""Download a video from Twitch"""
|
||||
if not (video_url := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("video_download_failed"))
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("video_download_started"))
|
||||
|
||||
with youtube_dl.YoutubeDL(
|
||||
{
|
||||
"outtmpl": "video.mp4",
|
||||
"format": "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
}
|
||||
) as ydl:
|
||||
ydl.download([video_url])
|
||||
|
||||
await utils.answer_file(message, "video.mp4")
|
||||
except Exception as e:
|
||||
await utils.answer(message, self.strings("video_download_failed"))
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
# reqires: youtube_dl
|
||||
|
||||
import youtube_dl
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TwitchModule(loader.Module):
|
||||
"""Module for downloading Twitch clips and videos"""
|
||||
|
||||
strings = {
|
||||
"name": "Twitchdl",
|
||||
"clip_download_started": (
|
||||
"<emoji document_id=5307773751796964107>⏳</emoji><b>Clip download started,"
|
||||
" please wait.</b>"
|
||||
),
|
||||
"clip_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Clip download"
|
||||
" completed. Sending...</b>"
|
||||
),
|
||||
"clip_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Clip download failed."
|
||||
" Ensure the correctness of the link.</b>"
|
||||
),
|
||||
"video_download_started": (
|
||||
"<b><emoji document_id=5307773751796964107>⏳</emoji>Video download"
|
||||
" started, please wait.</b>"
|
||||
),
|
||||
"video_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Video download"
|
||||
" completed. Sending...</b>"
|
||||
),
|
||||
"video_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Video download failed."
|
||||
" Ensure the correctness of the link.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"clip_download_started": (
|
||||
"<emoji document_id=5307773751796964107>⏳</emoji><b>Скачивание клипа"
|
||||
" началось, подождите.</b>"
|
||||
),
|
||||
"clip_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Скачивание клипа"
|
||||
" завершено. Отправляю...</b>"
|
||||
),
|
||||
"clip_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка скачивания"
|
||||
" клипа. Убедитесь в ссылке.</b>"
|
||||
),
|
||||
"video_download_started": (
|
||||
"<b><emoji document_id=5307773751796964107>⏳</emoji>Скачивание видео"
|
||||
" началось, подождите.</b>"
|
||||
),
|
||||
"video_download_completed": (
|
||||
"<b><emoji document_id=5215480011322042129>➡️</emoji>Скачивание видео"
|
||||
" завершено. Отправляю...</b>"
|
||||
),
|
||||
"video_download_failed": (
|
||||
"<b><emoji document_id=5980953710157632545>❌</emoji>Ошибка скачивания"
|
||||
" видео. Убедитесь в ссылке.</b>"
|
||||
),
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="Скачать клип с Twitch")
|
||||
async def twitch(self, message: Message):
|
||||
"""Download a clip from Twitch"""
|
||||
if not (clip_url := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("clip_download_failed"))
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("clip_download_started"))
|
||||
|
||||
with youtube_dl.YoutubeDL(
|
||||
{
|
||||
"outtmpl": "clip.mp4",
|
||||
"format": "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
}
|
||||
) as ydl:
|
||||
ydl.download([clip_url])
|
||||
|
||||
await utils.answer_file(message, "clip.mp4")
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings("clip_download_failed"))
|
||||
|
||||
@loader.command(ru_doc="Скачать видео с Twitch")
|
||||
async def twitchvideo(self, message: Message):
|
||||
"""Download a video from Twitch"""
|
||||
if not (video_url := utils.get_args_raw(message)):
|
||||
await utils.answer(message, self.strings("video_download_failed"))
|
||||
return
|
||||
|
||||
try:
|
||||
await utils.answer(message, self.strings("video_download_started"))
|
||||
|
||||
with youtube_dl.YoutubeDL(
|
||||
{
|
||||
"outtmpl": "video.mp4",
|
||||
"format": "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
}
|
||||
) as ydl:
|
||||
ydl.download([video_url])
|
||||
|
||||
await utils.answer_file(message, "video.mp4")
|
||||
except Exception as e:
|
||||
await utils.answer(message, self.strings("video_download_failed"))
|
||||
|
||||
@@ -1,117 +1,117 @@
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WatcherModule(loader.Module):
|
||||
"""Module for watching and responding to specific words"""
|
||||
|
||||
strings = {
|
||||
"name": "WatcherModule",
|
||||
"watch_added": (
|
||||
"<emoji document_id=5210956306952758910>👀</emoji>Watch added: {word}"
|
||||
),
|
||||
"watch_removed": (
|
||||
"<b><emoji document_id=5980930633298350051>✅</emoji>Watch removed:</b>"
|
||||
" {word}"
|
||||
),
|
||||
"watch_list": (
|
||||
"<b><emoji document_id=5818865088970362886>❕</emoji>List of watched"
|
||||
" words:</b>\n\n{watch_list}"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"watch_added": (
|
||||
"<b><emoji document_id=5210956306952758910>👀</emoji>Отслеживание"
|
||||
" добавлено:</b> {word}"
|
||||
),
|
||||
"watch_removed": (
|
||||
"<b><emoji document_id=5980930633298350051>✅</emoji>Отслеживание"
|
||||
" удалено:</b> {word}"
|
||||
),
|
||||
"watch_list": (
|
||||
"<b><emoji document_id=5818865088970362886>❕</emoji>Список отслеживаемых"
|
||||
" слов:</b>\n\n{watch_list}"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._watches = {}
|
||||
|
||||
async def client_ready(self):
|
||||
self._load_watches()
|
||||
|
||||
async def watcher(self, message: Message):
|
||||
"""Watcher method to track and respond to specific words"""
|
||||
if getattr(message, "text", None):
|
||||
text = message.text.lower()
|
||||
for word, response in self._watches.items():
|
||||
if word.lower() == text:
|
||||
await message.reply(response)
|
||||
|
||||
@loader.command(ru_doc="Добавить отслеживание слова")
|
||||
async def addwatch(self, message: Message):
|
||||
"""Add a word to be watched"""
|
||||
if len(args := utils.get_args_split_by(message, "$")) < 2:
|
||||
await utils.answer(
|
||||
message,
|
||||
(
|
||||
"<emoji document_id=5978859389614821335>❌</emoji> Не указан один"
|
||||
' из аргументов! Пример правильной команды: `.addwatch "первое'
|
||||
' слово"$"ответ на него"`'
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
word = args[0].strip()
|
||||
response = args[1].strip()
|
||||
self._watches[word] = response
|
||||
self._save_watches()
|
||||
await utils.answer(message, self.strings("watch_added").format(word=word))
|
||||
|
||||
@loader.command(ru_doc="Удалить отслеживание слова")
|
||||
async def rmwatch(self, m: Message):
|
||||
"""Remove a word from being watched"""
|
||||
if (word := utils.get_args_raw(m)) not in self._watches:
|
||||
await utils.answer(m, f"Watch not found for word: {word}")
|
||||
return
|
||||
|
||||
del self._watches[word]
|
||||
self._save_watches()
|
||||
await utils.answer(m, self.strings("watch_removed").format(word=word))
|
||||
|
||||
@loader.command(ru_doc="Показать список отслеживаемых слов")
|
||||
async def listwatches(self, message: Message):
|
||||
"""Show the list of watched words"""
|
||||
if not self._watches:
|
||||
await utils.answer(message, "No watches found")
|
||||
return
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("watch_list").format(
|
||||
watch_list="\n".join(
|
||||
f"{word}: {response}" for word, response in self._watches.items()
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def _save_watches(self):
|
||||
self.set("watches", self._watches)
|
||||
|
||||
def _load_watches(self):
|
||||
self._watches = self.get("watches", {})
|
||||
# ╔╗╔┌─┐┬─┐┌─┐┬ ┬
|
||||
# ║║║├┤ ├┬┘│ └┬┘
|
||||
# ╝╚╝└─┘┴└─└─┘ ┴
|
||||
|
||||
# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
# You CANNOT edit this file without direct permission from the author.
|
||||
# You can redistribute this file without any changes.
|
||||
|
||||
# meta developer: @nercymods
|
||||
# scope: hikka_min 1.6.2
|
||||
|
||||
from hikkatl.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WatcherModule(loader.Module):
|
||||
"""Module for watching and responding to specific words"""
|
||||
|
||||
strings = {
|
||||
"name": "WatcherModule",
|
||||
"watch_added": (
|
||||
"<emoji document_id=5210956306952758910>👀</emoji>Watch added: {word}"
|
||||
),
|
||||
"watch_removed": (
|
||||
"<b><emoji document_id=5980930633298350051>✅</emoji>Watch removed:</b>"
|
||||
" {word}"
|
||||
),
|
||||
"watch_list": (
|
||||
"<b><emoji document_id=5818865088970362886>❕</emoji>List of watched"
|
||||
" words:</b>\n\n{watch_list}"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"watch_added": (
|
||||
"<b><emoji document_id=5210956306952758910>👀</emoji>Отслеживание"
|
||||
" добавлено:</b> {word}"
|
||||
),
|
||||
"watch_removed": (
|
||||
"<b><emoji document_id=5980930633298350051>✅</emoji>Отслеживание"
|
||||
" удалено:</b> {word}"
|
||||
),
|
||||
"watch_list": (
|
||||
"<b><emoji document_id=5818865088970362886>❕</emoji>Список отслеживаемых"
|
||||
" слов:</b>\n\n{watch_list}"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._watches = {}
|
||||
|
||||
async def client_ready(self):
|
||||
self._load_watches()
|
||||
|
||||
async def watcher(self, message: Message):
|
||||
"""Watcher method to track and respond to specific words"""
|
||||
if getattr(message, "text", None):
|
||||
text = message.text.lower()
|
||||
for word, response in self._watches.items():
|
||||
if word.lower() == text:
|
||||
await message.reply(response)
|
||||
|
||||
@loader.command(ru_doc="Добавить отслеживание слова")
|
||||
async def addwatch(self, message: Message):
|
||||
"""Add a word to be watched"""
|
||||
if len(args := utils.get_args_split_by(message, "$")) < 2:
|
||||
await utils.answer(
|
||||
message,
|
||||
(
|
||||
"<emoji document_id=5978859389614821335>❌</emoji> Не указан один"
|
||||
' из аргументов! Пример правильной команды: `.addwatch "первое'
|
||||
' слово"$"ответ на него"`'
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
word = args[0].strip()
|
||||
response = args[1].strip()
|
||||
self._watches[word] = response
|
||||
self._save_watches()
|
||||
await utils.answer(message, self.strings("watch_added").format(word=word))
|
||||
|
||||
@loader.command(ru_doc="Удалить отслеживание слова")
|
||||
async def rmwatch(self, m: Message):
|
||||
"""Remove a word from being watched"""
|
||||
if (word := utils.get_args_raw(m)) not in self._watches:
|
||||
await utils.answer(m, f"Watch not found for word: {word}")
|
||||
return
|
||||
|
||||
del self._watches[word]
|
||||
self._save_watches()
|
||||
await utils.answer(m, self.strings("watch_removed").format(word=word))
|
||||
|
||||
@loader.command(ru_doc="Показать список отслеживаемых слов")
|
||||
async def listwatches(self, message: Message):
|
||||
"""Show the list of watched words"""
|
||||
if not self._watches:
|
||||
await utils.answer(message, "No watches found")
|
||||
return
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("watch_list").format(
|
||||
watch_list="\n".join(
|
||||
f"{word}: {response}" for word, response in self._watches.items()
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def _save_watches(self):
|
||||
self.set("watches", self._watches)
|
||||
|
||||
def _load_watches(self):
|
||||
self._watches = self.get("watches", {})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user