mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Commited backup
This commit is contained in:
33
GeekTG/FTG-Modules/.github/workflows/python-app.yml
vendored
Normal file
33
GeekTG/FTG-Modules/.github/workflows/python-app.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: Python application
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8 pytest
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
3
GeekTG/FTG-Modules/.gitignore
vendored
Normal file
3
GeekTG/FTG-Modules/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
.ruff_cache/
|
||||
3
GeekTG/FTG-Modules/README.md
Normal file
3
GeekTG/FTG-Modules/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Friendly-Telegram Modules
|
||||
|
||||
## FTG Modules for [GeekTG/Friendly-Telegram](https://github.com/GeekTG/Friendly-Telegram)
|
||||
645
GeekTG/FTG-Modules/admin_tools.py
Normal file
645
GeekTG/FTG-Modules/admin_tools.py
Normal file
@@ -0,0 +1,645 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @Fl1yd
|
||||
|
||||
import io
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
from telethon.errors import (
|
||||
ChatAdminRequiredError,
|
||||
PhotoCropSizeSmallError,
|
||||
UserAdminInvalidError,
|
||||
)
|
||||
from telethon.tl.functions.channels import (
|
||||
EditAdminRequest,
|
||||
EditBannedRequest,
|
||||
EditPhotoRequest,
|
||||
)
|
||||
from telethon.tl.functions.messages import EditChatAdminRequest
|
||||
from telethon.tl.types import ChatAdminRights, ChatBannedRights
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
# ================== CONSTANS ========================
|
||||
|
||||
DEMOTE_RIGHTS = ChatAdminRights(
|
||||
post_messages=None,
|
||||
add_admins=None,
|
||||
invite_users=None,
|
||||
change_info=None,
|
||||
ban_users=None,
|
||||
delete_messages=None,
|
||||
pin_messages=None,
|
||||
edit_messages=None,
|
||||
)
|
||||
|
||||
UNMUTE_RIGHTS = ChatBannedRights(
|
||||
until_date=None,
|
||||
view_messages=None,
|
||||
send_messages=False,
|
||||
send_media=False,
|
||||
send_stickers=False,
|
||||
send_gifs=False,
|
||||
send_games=False,
|
||||
send_inline=False,
|
||||
embed_links=False,
|
||||
)
|
||||
|
||||
BANNED_RIGHTS = ChatBannedRights(
|
||||
until_date=None,
|
||||
view_messages=True,
|
||||
send_messages=True,
|
||||
send_media=True,
|
||||
send_stickers=True,
|
||||
send_gifs=True,
|
||||
send_games=True,
|
||||
send_inline=True,
|
||||
embed_links=True,
|
||||
)
|
||||
|
||||
UNBAN_RIGHTS = ChatBannedRights(
|
||||
until_date=None,
|
||||
view_messages=None,
|
||||
send_messages=None,
|
||||
send_media=None,
|
||||
send_stickers=None,
|
||||
send_gifs=None,
|
||||
send_games=None,
|
||||
send_inline=None,
|
||||
embed_links=None,
|
||||
)
|
||||
|
||||
|
||||
# =====================================================
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AdminToolsMod(loader.Module):
|
||||
"""Admin Tools"""
|
||||
|
||||
strings = {
|
||||
"name": "Admin Tools",
|
||||
"not_pic": "<b>This isn`t an pic/sticker.</b>",
|
||||
"wait": "<b>Waiting...</b>",
|
||||
"pic_so_small": "<b>The image is too small, try another one.</b>",
|
||||
"pic_changed": "<b>Chat pic changed.</b>",
|
||||
"promote_none": "<b>No one to promote.</b>",
|
||||
"who": "<b>Who is it?</b>",
|
||||
"not_admin": "<b>I`m not an admin here.</b>",
|
||||
"promoted": "<b>{} promoted to admin rights.\nRank: {}</b>",
|
||||
"wtf_is_it": "<b>What is it?</b>",
|
||||
"this_isn`t_a_chat": "<b>This isn`t a chat!</b>",
|
||||
"demote_none": "<b>No one to demote.</b>",
|
||||
"demoted": "<b>{} demoted to admin rights.</b>",
|
||||
"pinning": "<b>Pin...</b>",
|
||||
"pin_none": "<b>Reply to the message to pin it.</b>",
|
||||
"unpinning": "<b>Unpin...</b>",
|
||||
"unpin_none": "<b>Nothing to unpin.</b>",
|
||||
"no_rights": "<b>I don`t have rights.</b>",
|
||||
"pinned": "<b>Pinned successfully!</b>",
|
||||
"unpinned": "<b>Unpinned successfully!</b>",
|
||||
"can`t_kick": "<b>Can`t kick.</b>",
|
||||
"kicking": "<b>Kick...</b>",
|
||||
"kick_none": "<b>No one to kick.</b>",
|
||||
"kicked": "<b>{} kicked from chat.</b>",
|
||||
"kicked_for_reason": "<b>{} kicked from chat.\nReason: {}.</b>",
|
||||
"banning": "<b>Ban...</b>",
|
||||
"banned": "<b>{} banned in chat.</b>",
|
||||
"banned_for_reason": "<b>{} banned in chat.\nReason: {}</b>",
|
||||
"ban_none": "<b>No one to ban.</b>",
|
||||
"unban_none": "<b>No one to unban.</b>",
|
||||
"unbanned": "<b>{} unbanned in chat.</b>",
|
||||
"mute_none": "<b>No one to mute.</b>",
|
||||
"muted": "<b>{} now muted for </b>",
|
||||
"no_args": "<b>Invalid arguments specified.</b>",
|
||||
"unmute_none": "<b>No one to unmute.</b>",
|
||||
"unmuted": "<b>{} now unmuted.</b>",
|
||||
"no_reply": "<b>No reply.</b>",
|
||||
"del_u_search": "<b>Search for deleted accounts...</b>",
|
||||
"del_u_kicking": "<b>Kick deleted accounts...\nOh~, I can do it?!</b>",
|
||||
}
|
||||
|
||||
async def ecpcmd(self, message):
|
||||
"""Command .ecp changes the pic of the chat.\nUse: .ecp <reply to pic/sticker>."""
|
||||
if not message.chat:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not reply:
|
||||
return await utils.answer(message, self.strings("no_reply", message))
|
||||
|
||||
pic = await check_media(message, reply)
|
||||
if not pic:
|
||||
return await utils.answer(message, self.strings("not_pic", message))
|
||||
await utils.answer(message, self.strings("wait", message))
|
||||
|
||||
what = resizepic(pic)
|
||||
if what:
|
||||
try:
|
||||
await message.client(
|
||||
EditPhotoRequest(
|
||||
message.chat_id, await message.client.upload_file(what)
|
||||
)
|
||||
)
|
||||
except PhotoCropSizeSmallError:
|
||||
return await utils.answer(
|
||||
message, self.strings("pic_so_small", message)
|
||||
)
|
||||
await utils.answer(message, self.strings("pic_changed", message))
|
||||
except ChatAdminRequiredError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
async def promotecmd(self, message):
|
||||
"""Command .promote for promote user to admin rights.\nUse: .promote <@ or reply> <rank>."""
|
||||
if not message.chat:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
args = utils.get_args_raw(message).split(" ")
|
||||
reply = await message.get_reply_message()
|
||||
rank = "admin"
|
||||
|
||||
chat = await message.get_chat()
|
||||
adm_rights = chat.admin_rights
|
||||
if not adm_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if reply:
|
||||
args = utils.get_args_raw(message)
|
||||
rank = args or rank
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
if len(args) == 1:
|
||||
rank = rank
|
||||
elif len(args) >= 2:
|
||||
rank = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
try:
|
||||
await message.client(
|
||||
EditAdminRequest(
|
||||
message.chat_id,
|
||||
user.id,
|
||||
ChatAdminRights(
|
||||
add_admins=False,
|
||||
invite_users=adm_rights.invite_users,
|
||||
change_info=False,
|
||||
ban_users=adm_rights.ban_users,
|
||||
delete_messages=adm_rights.delete_messages,
|
||||
pin_messages=adm_rights.pin_messages,
|
||||
),
|
||||
rank,
|
||||
)
|
||||
)
|
||||
except ChatAdminRequiredError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
else:
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("promoted", message).format(user.first_name, rank),
|
||||
)
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
async def demotecmd(self, message):
|
||||
"""Command .demote for demote user to admin rights.\nUse: .demote <@ or reply>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await utils.answer(
|
||||
message, self.strings("demote_none", message)
|
||||
)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
|
||||
try:
|
||||
if message.is_channel:
|
||||
await message.client(
|
||||
EditAdminRequest(message.chat_id, user.id, DEMOTE_RIGHTS, "")
|
||||
)
|
||||
else:
|
||||
await message.client(
|
||||
EditChatAdminRequest(message.chat_id, user.id, False)
|
||||
)
|
||||
except ChatAdminRequiredError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
else:
|
||||
return await utils.answer(
|
||||
message, self.strings("demoted", message).format(user.first_name)
|
||||
)
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args"))
|
||||
|
||||
async def pincmd(self, message):
|
||||
"""Command .pin for pin message in the chat.\nUse: .pin <reply>."""
|
||||
if message.is_private:
|
||||
await utils.answer(message, self.strings("this_isn`t_a_chat", message))
|
||||
return
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
return await utils.answer(message, self.strings("pin_none", message))
|
||||
|
||||
await utils.answer(message, self.strings("pinning", message))
|
||||
try:
|
||||
await message.client.pin_message(
|
||||
message.chat, message=reply.id, notify=False
|
||||
)
|
||||
except ChatAdminRequiredError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
await utils.answer(message, self.strings("pinned", message))
|
||||
|
||||
async def unpincmd(self, message):
|
||||
"""Command .unpin for unpin message in the chat.\nUse: .unpin."""
|
||||
if message.is_private:
|
||||
await utils.answer(message, self.strings("this_isn`t_a_chat", message))
|
||||
return
|
||||
|
||||
await utils.answer(message, self.strings("unpinning", message))
|
||||
|
||||
try:
|
||||
await message.client.pin_message(message.chat, message=None, notify=None)
|
||||
except ChatAdminRequiredError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
await utils.answer(message, self.strings("unpinned", message))
|
||||
|
||||
async def kickcmd(self, message):
|
||||
"""Command .kick for kick the user.\nUse: .kick <@ or reply>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
args = utils.get_args_raw(message).split(" ")
|
||||
reason = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
args = utils.get_args_raw(message)
|
||||
if args:
|
||||
reason = args
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
if args:
|
||||
if len(args) == 1:
|
||||
args = utils.get_args_raw(message)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
reason = False
|
||||
elif len(args) >= 2:
|
||||
reason = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
|
||||
await utils.answer(message, self.strings("kicking", message))
|
||||
try:
|
||||
await message.client.kick_participant(message.chat_id, user.id)
|
||||
except UserAdminInvalidError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
if not reason:
|
||||
return await utils.answer(
|
||||
message, self.strings("kicked", message).format(user.first_name)
|
||||
)
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("kicked_for_reason", message).format(
|
||||
user.first_name, reason
|
||||
),
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
async def bancmd(self, message):
|
||||
"""Command .ban for ban the user.\nUse: .ban <@ or reply>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
args = utils.get_args_raw(message).split(" ")
|
||||
reason = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
args = utils.get_args_raw(message)
|
||||
if args:
|
||||
reason = args
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
if args:
|
||||
if len(args) == 1:
|
||||
args = utils.get_args_raw(message)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
reason = False
|
||||
elif len(args) >= 2:
|
||||
reason = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
try:
|
||||
await utils.answer(message, self.strings("banning", message))
|
||||
await message.client(
|
||||
EditBannedRequest(
|
||||
message.chat_id,
|
||||
user.id,
|
||||
ChatBannedRights(until_date=None, view_messages=True),
|
||||
)
|
||||
)
|
||||
except UserAdminInvalidError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
if not reason:
|
||||
return await utils.answer(
|
||||
message, self.strings("banned", message).format(user.first_name)
|
||||
)
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("banned_for_reason", message).format(
|
||||
user.first_name, reason
|
||||
),
|
||||
)
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
async def unbancmd(self, message):
|
||||
"""Command .unban for unban the user.\nUse: .unban <@ or reply>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await utils.answer(
|
||||
message, self.strings("unban_none", message)
|
||||
)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
await message.client(
|
||||
EditBannedRequest(
|
||||
message.chat_id,
|
||||
user.id,
|
||||
ChatBannedRights(until_date=None, view_messages=False),
|
||||
)
|
||||
)
|
||||
|
||||
return await utils.answer(
|
||||
message, self.strings("unbanned", message).format(user.first_name)
|
||||
)
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
async def mutecmd(self, message):
|
||||
"""Command .mute for mute the user.\nUse: .mute <@ or reply> <time (1m, 1h, 1d)>."""
|
||||
if message.is_private:
|
||||
await utils.answer(message, self.strings("this_isn`t_a_chat", message))
|
||||
return
|
||||
|
||||
args = utils.get_args_raw(message).split()
|
||||
reply = await message.get_reply_message()
|
||||
timee = False
|
||||
|
||||
try:
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
args = utils.get_args_raw(message)
|
||||
if args:
|
||||
timee = args
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
if args:
|
||||
if len(args) == 1:
|
||||
args = utils.get_args_raw(message)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
timee = False
|
||||
elif len(args) >= 2:
|
||||
timee = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
if timee:
|
||||
n = ""
|
||||
t = ""
|
||||
|
||||
for _ in timee:
|
||||
if _.isdigit():
|
||||
n += _
|
||||
else:
|
||||
t += _
|
||||
|
||||
text = f"<b>{n}"
|
||||
|
||||
if t == "m":
|
||||
n = int(n) * 60
|
||||
text += " m.</b>"
|
||||
|
||||
elif t == "h":
|
||||
n = int(n) * 3600
|
||||
text += " h.</b>"
|
||||
|
||||
elif t == "d":
|
||||
n = int(n) * 86400
|
||||
text += " d.</b>"
|
||||
|
||||
else:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
try:
|
||||
tm = ChatBannedRights(
|
||||
until_date=time.time() + int(n), send_messages=True
|
||||
)
|
||||
await message.client(EditBannedRequest(message.chat_id, user.id, tm))
|
||||
return await utils.answer(
|
||||
message,
|
||||
self.strings("muted", message).format(user.first_name) + text,
|
||||
)
|
||||
except UserAdminInvalidError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
else:
|
||||
try:
|
||||
tm = ChatBannedRights(until_date=True, send_messages=True)
|
||||
await message.client(EditBannedRequest(message.chat_id, user.id, tm))
|
||||
return await message.edit(
|
||||
"<b>{} is now muted.</b>".format(user.first_name)
|
||||
)
|
||||
except UserAdminInvalidError:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
async def unmutecmd(self, message):
|
||||
"""Command .unmute for unmute the user.\nUse: .unmute <@ or reply>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
try:
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await utils.answer(
|
||||
message, self.strings("unmute_none", message)
|
||||
)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
await message.client(
|
||||
EditBannedRequest(message.chat_id, user.id, UNMUTE_RIGHTS)
|
||||
)
|
||||
|
||||
return await utils.answer(
|
||||
message, self.strings("unmuted", message).format(user.first_name)
|
||||
)
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("no_args", message))
|
||||
|
||||
async def deluserscmd(self, message):
|
||||
"""Command .delusers shows a list of all deleted accounts in the chat.\nUse: .delusers <clean>."""
|
||||
if message.is_private:
|
||||
return await utils.answer(
|
||||
message, self.strings("this_isn`t_a_chat", message)
|
||||
)
|
||||
|
||||
con = utils.get_args_raw(message)
|
||||
del_status = "<b>No deleted accouns found.</b>"
|
||||
|
||||
if con != "clean":
|
||||
await utils.answer(message, self.strings("del_u_search", message))
|
||||
del_u = len(
|
||||
[
|
||||
_
|
||||
async for _ in message.client.iter_participants(message.chat_id)
|
||||
if _.deleted
|
||||
]
|
||||
)
|
||||
del_status = f"<b>Found {del_u} deleted accounts, clear them with </b><code>.delusers clean</code><b>.</b>"
|
||||
return await message.edit(del_status)
|
||||
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await utils.answer(message, self.strings("not_admin", message))
|
||||
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await utils.answer(message, self.strings("no_rights", message))
|
||||
|
||||
await utils.answer(message, self.strings("del_u_kicking", message))
|
||||
del_u = 0
|
||||
del_a = 0
|
||||
async for user in message.client.iter_participants(message.chat_id):
|
||||
if user.deleted:
|
||||
try:
|
||||
await message.client(
|
||||
EditBannedRequest(message.chat_id, user.id, BANNED_RIGHTS)
|
||||
)
|
||||
except UserAdminInvalidError:
|
||||
del_u -= 1
|
||||
del_a += 1
|
||||
await message.client(
|
||||
EditBannedRequest(message.chat_id, user.id, UNBAN_RIGHTS)
|
||||
)
|
||||
del_u += 1
|
||||
if del_u > 0:
|
||||
del_status = f"<b>Kicked {del_u} deleted account(s).</b>"
|
||||
|
||||
if del_a > 0:
|
||||
del_status = (
|
||||
f"<b>Kicked {del_u} deleted account(s).\n"
|
||||
f"{del_a} deleted admins account are not kicked.</b>"
|
||||
)
|
||||
await message.edit(del_status)
|
||||
|
||||
|
||||
def resizepic(reply):
|
||||
im = Image.open(io.BytesIO(reply))
|
||||
w, h = im.size
|
||||
x = min(w, h)
|
||||
x_ = (w - x) // 2
|
||||
y_ = (h - x) // 2
|
||||
_x = x_ + x
|
||||
_y = y_ + x
|
||||
im = im.crop((x_, y_, _x, _y))
|
||||
out = io.BytesIO()
|
||||
out.name = "outsider.png"
|
||||
im.save(out)
|
||||
return out.getvalue()
|
||||
|
||||
|
||||
async def check_media(message, reply):
|
||||
data = reply.photo or reply.sticker if reply else None
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = await message.client.download_file(data, bytes)
|
||||
try:
|
||||
Image.open(io.BytesIO(data))
|
||||
return data
|
||||
except Exception:
|
||||
return False
|
||||
347
GeekTG/FTG-Modules/audio_editor.py
Normal file
347
GeekTG/FTG-Modules/audio_editor.py
Normal file
@@ -0,0 +1,347 @@
|
||||
# .------.------.------.------.------.------.------.------.------.------.
|
||||
# |D.--. |4.--. |N.--. |1.--. |3.--. |L.--. |3.--. |K.--. |0.--. |0.--. |
|
||||
# | :/\: | :/\: | :(): | :/\: | :(): | :/\: | :(): | :/\: | :/\: | :/\: |
|
||||
# | (__) | :\/: | ()() | (__) | ()() | (__) | ()() | :\/: | :\/: | :\/: |
|
||||
# | '--'D| '--'4| '--'N| '--'1| '--'3| '--'L| '--'3| '--'K| '--'0| '--'0|
|
||||
# `------`------`------`------`------`------`------`------`------`------'
|
||||
#
|
||||
# Copyright 2022 t.me/D4n13l3k00
|
||||
# Licensed under the Creative Commons CC BY-NC-ND 4.0
|
||||
#
|
||||
# Full license text can be found at:
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
||||
#
|
||||
# Human-friendly one:
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0
|
||||
|
||||
# meta developer: @D4n13l3k00
|
||||
|
||||
|
||||
# requires: pydub numpy requests
|
||||
|
||||
import io
|
||||
import math
|
||||
import re
|
||||
|
||||
import aiohttp
|
||||
import numpy as np
|
||||
from pydub import AudioSegment, effects
|
||||
from telethon import types
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AudioEditorMod(loader.Module):
|
||||
"""Module for working with sound"""
|
||||
|
||||
strings = {
|
||||
"name": "AudioEditor",
|
||||
"downloading": "<b>[{}]</b> Downloading...",
|
||||
"working": "<b>[{}]</b> Working...",
|
||||
"exporting": "<b>[{}]</b> Exporting...",
|
||||
"set_value": "<b>[{}]</b> Specify the level from {} to {}...",
|
||||
"reply": "<b>[{}]</b> reply to audio...",
|
||||
"set_fmt": "<b>[{}]</b> Specify the format of output audio...",
|
||||
"set_time": "<b>[{}]</b> Specify the time in the format start(ms):end(ms)",
|
||||
}
|
||||
|
||||
@loader.owner
|
||||
async def basscmd(self, m):
|
||||
""".bass [level bass'а 2-100 (Default 2)] <reply to audio>
|
||||
BassBoost"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
lvl = 2.0
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (1.0 < float(args) < 100.1):
|
||||
lvl = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("BassBoost", 2.0, 100.0)
|
||||
)
|
||||
audio = await self.get_audio(m, "BassBoost")
|
||||
if not audio:
|
||||
return
|
||||
sample_track = list(audio.audio.get_array_of_samples())
|
||||
out = (audio.audio - 0).overlay(
|
||||
audio.audio.low_pass_filter(
|
||||
int(
|
||||
round(
|
||||
(
|
||||
3 * np.std(sample_track) / (math.sqrt(2))
|
||||
- np.mean(sample_track)
|
||||
)
|
||||
* 0.005
|
||||
)
|
||||
)
|
||||
)
|
||||
+ lvl
|
||||
)
|
||||
await self.send_audio(m, audio, out, audio.pref, f"{audio.pref} {lvl}lvl")
|
||||
|
||||
@loader.owner
|
||||
async def fvcmd(self, m):
|
||||
""".fv [level 2-100 (Default 25)] <reply to audio>
|
||||
Distort"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
lvl = 25.0
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (1.0 < float(args) < 100.1):
|
||||
lvl = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Distort", 2.0, 100.0)
|
||||
)
|
||||
audio = await self.get_audio(m, "Distort")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio + lvl
|
||||
await self.send_audio(m, audio, out, audio.pref, f"{audio.pref} {lvl}lvl")
|
||||
|
||||
@loader.owner
|
||||
async def echoscmd(self, m):
|
||||
""".echos <reply to audio>
|
||||
Echo effect"""
|
||||
audio = await self.get_audio(m, "Echo")
|
||||
if not audio:
|
||||
return
|
||||
out = AudioSegment.empty()
|
||||
n = 200
|
||||
none = io.BytesIO()
|
||||
out += audio.audio + AudioSegment.from_file(none)
|
||||
for _ in range(5):
|
||||
audio.audio - 10
|
||||
out = out.overlay(audio.audio, n)
|
||||
n += 200
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def volupcmd(self, m):
|
||||
""".volup <reply to audio>
|
||||
VolUp 10dB"""
|
||||
audio = await self.get_audio(m, "+10dB")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio + 10
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def voldwcmd(self, m):
|
||||
""".voldw <reply to audio>
|
||||
VolDw 10dB"""
|
||||
audio = await self.get_audio(m, "-10dB")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio - 10
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def revscmd(self, m):
|
||||
""".revs <reply to audio>
|
||||
Reverse audio"""
|
||||
audio = await self.get_audio(m, "Reverse")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio.reverse()
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def repscmd(self, m):
|
||||
""".reps <reply to audio>
|
||||
Repeat audio 2 times"""
|
||||
audio = await self.get_audio(m, "Repeat")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio * 2
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def slowscmd(self, m):
|
||||
""".slows <reply to audio>
|
||||
SlowDown 0.5x"""
|
||||
audio = await self.get_audio(m, "SlowDown")
|
||||
if not audio:
|
||||
return
|
||||
s2 = audio.audio._spawn(
|
||||
audio.audio.raw_data,
|
||||
overrides={"frame_rate": int(audio.audio.frame_rate * 0.5)},
|
||||
)
|
||||
out = s2.set_frame_rate(audio.audio.frame_rate)
|
||||
await self.send_audio(
|
||||
audio.message, audio, out, audio.pref, audio.pref, audio.duration * 2
|
||||
)
|
||||
|
||||
@loader.owner
|
||||
async def fastscmd(self, m):
|
||||
""".fasts <reply to audio>
|
||||
SpeedUp 1.5x"""
|
||||
audio = await self.get_audio(m, "SpeedUp")
|
||||
if not audio:
|
||||
return
|
||||
s2 = audio.audio._spawn(
|
||||
audio.audio.raw_data,
|
||||
overrides={"frame_rate": int(audio.audio.frame_rate * 1.5)},
|
||||
)
|
||||
out = s2.set_frame_rate(audio.audio.frame_rate)
|
||||
await self.send_audio(
|
||||
audio.message,
|
||||
audio,
|
||||
out,
|
||||
audio.pref,
|
||||
audio.pref,
|
||||
round(audio.duration / 2),
|
||||
)
|
||||
|
||||
@loader.owner
|
||||
async def rightscmd(self, m):
|
||||
""".rights <reply to audio>
|
||||
Push sound to right channel"""
|
||||
audio = await self.get_audio(m, "Right channel")
|
||||
if not audio:
|
||||
return
|
||||
out = effects.pan(audio.audio, +1.0)
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def leftscmd(self, m):
|
||||
""".lefts <reply to audio>
|
||||
Push sound to left channel"""
|
||||
audio = await self.get_audio(m, "Left channel")
|
||||
if not audio:
|
||||
return
|
||||
out = effects.pan(audio.audio, -1.0)
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def normscmd(self, m):
|
||||
""".norms <reply to audio>
|
||||
Normalize sound (from quiet to normal)"""
|
||||
audio = await self.get_audio(m, "Normalization")
|
||||
if not audio:
|
||||
return
|
||||
out = effects.normalize(audio.audio)
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def tovscmd(self, m):
|
||||
""".tovs <reply to audio>
|
||||
Convert to voice message"""
|
||||
utils.get_args_raw(m)
|
||||
audio = await self.get_audio(m, "Voice")
|
||||
if not audio:
|
||||
return
|
||||
audio.voice = True
|
||||
await self.send_audio(audio.message, audio, audio.audio, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def convscmd(self, m):
|
||||
""".convs <reply to audio> [audio_format (ex. `mp3`)]
|
||||
Convert audio to some format"""
|
||||
f = utils.get_args(m)
|
||||
if not f:
|
||||
return await utils.answer(m, self.strings("set_fmt", m).format("Converter"))
|
||||
audio = await self.get_audio(m, "Converter")
|
||||
if not audio:
|
||||
return
|
||||
await self.send_audio(
|
||||
audio.message,
|
||||
audio,
|
||||
audio.audio,
|
||||
audio.pref,
|
||||
f"Converted to {f[0].lower()}",
|
||||
fmt=f[0].lower(),
|
||||
)
|
||||
|
||||
@loader.owner
|
||||
async def byrobertscmd(self, m):
|
||||
'''.byroberts <reply to audio>
|
||||
Add at the end "Directed by Robert B Weide"'''
|
||||
audio = await self.get_audio(m, "Directed by...")
|
||||
if not audio:
|
||||
return
|
||||
async with aiohttp.ClientSession() as s, s.get(
|
||||
"https://raw.githubusercontent.com/D4n13l3k00/files-for-modules/master/directed.mp3"
|
||||
) as r:
|
||||
out = audio.audio + AudioSegment.from_file(
|
||||
io.BytesIO(await r.read())
|
||||
).apply_gain(+8)
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
@loader.owner
|
||||
async def cutscmd(self, m):
|
||||
""".cuts <start(ms):end(ms)> <reply to audio>
|
||||
Cut audio"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
return await utils.answer(m, self.strings("set_time", m).format("Cut"))
|
||||
r = re.compile(r"^(?P<start>\d+){0,1}:(?P<end>\d+){0,1}$")
|
||||
ee = r.match(args)
|
||||
if not ee:
|
||||
return await utils.answer(m, self.strings("set_time", m).format("Cut"))
|
||||
start = int(ee["start"]) if ee["start"] else 0
|
||||
end = int(ee["end"]) if ee["end"] else 0
|
||||
audio = await self.get_audio(m, "Cut")
|
||||
if not audio:
|
||||
return
|
||||
out = audio.audio[start : end or len(audio.audio) - 1]
|
||||
await self.send_audio(audio.message, audio, out, audio.pref, audio.pref)
|
||||
|
||||
class AudioEditorClass:
|
||||
audio = None
|
||||
message = None
|
||||
duration = None
|
||||
voice = None
|
||||
pref = None
|
||||
reply = None
|
||||
|
||||
async def get_audio(self, m, pref):
|
||||
r = await m.get_reply_message()
|
||||
if r and r.file and r.file.mime_type.split("/")[0] in ["audio", "video"]:
|
||||
ae = self.AudioEditorClass()
|
||||
ae.pref = pref
|
||||
ae.reply = r
|
||||
ae.voice = (
|
||||
r.document.attributes[0].voice
|
||||
if r.file.mime_type.split("/")[0] == "audio"
|
||||
else False
|
||||
)
|
||||
ae.duration = r.document.attributes[0].duration
|
||||
ae.message = await utils.answer(
|
||||
m, self.strings("downloading", m).format(pref)
|
||||
)
|
||||
ae.audio = AudioSegment.from_file(io.BytesIO(await r.download_media(bytes)))
|
||||
ae.message = await utils.answer(
|
||||
ae.message, self.strings("working", m).format(pref)
|
||||
)
|
||||
return ae
|
||||
await utils.answer(m, self.strings("reply", m).format(pref))
|
||||
return None
|
||||
|
||||
async def send_audio(self, message, audio, out, pref, title, fs=None, fmt="mp3"):
|
||||
out_file = io.BytesIO()
|
||||
out_file.name = "audio." + ("ogg" if audio.voice else "mp3")
|
||||
if audio.voice:
|
||||
out.split_to_mono()
|
||||
message = await utils.answer(message, self.strings("exporting").format(pref))
|
||||
out.export(
|
||||
out_file,
|
||||
format="ogg" if audio.voice else fmt,
|
||||
bitrate="64k" if audio.voice else None,
|
||||
codec="libopus" if audio.voice else None,
|
||||
)
|
||||
out_file.seek(0)
|
||||
await utils.answer(
|
||||
message,
|
||||
out_file,
|
||||
reply_to=audio.reply.id,
|
||||
voice_note=audio.voice,
|
||||
attributes=None
|
||||
if audio.voice
|
||||
else [
|
||||
types.DocumentAttributeAudio(
|
||||
duration=fs or audio.duration,
|
||||
title=title,
|
||||
performer="AudioEditor",
|
||||
)
|
||||
],
|
||||
)
|
||||
228
GeekTG/FTG-Modules/autoprofile.py
Normal file
228
GeekTG/FTG-Modules/autoprofile.py
Normal file
@@ -0,0 +1,228 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import ast
|
||||
import asyncio
|
||||
import time
|
||||
from io import BytesIO
|
||||
|
||||
from telethon.tl import functions
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
pil_installed = False
|
||||
else:
|
||||
pil_installed = True
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AutoProfileMod(loader.Module):
|
||||
"""Automatic stuff for your profile :P"""
|
||||
|
||||
strings = {
|
||||
"name": "Automatic Profile",
|
||||
"missing_pil": "<b>You don't have Pillow installed</b>",
|
||||
"missing_pfp": "<b>You don't have a profile picture to rotate</b>",
|
||||
"invalid_args": "<b>Missing parameters, please read the docs</b>",
|
||||
"invalid_degrees": "<b>Invalid number of degrees to rotate, please read the docs</b>",
|
||||
"invalid_delete": "<b>Please specify whether to delete the old pictures or not</b>",
|
||||
"enabled_pfp": "<b>Enabled profile picture rotation</b>",
|
||||
"pfp_not_enabled": "<b>Profile picture rotation is not enabled</b>",
|
||||
"pfp_disabled": "<b>Profile picture rotation disabled</b>",
|
||||
"missing_time": "<b>Time was not specified in bio</b>",
|
||||
"enabled_bio": "<b>Enabled bio clock</b>",
|
||||
"bio_not_enabled": "<b>Bio clock is not enabled</b>",
|
||||
"disabled_bio": "<b>Disabled bio clock</b>",
|
||||
"enabled_name": "<b>Enabled name clock</b>",
|
||||
"name_not_enabled": "<b>Name clock is not enabled</b>",
|
||||
"disabled_name": "<b>Name clock disabled</b>",
|
||||
"how_many_pfps": "<b>Please specify how many profile pictures should be removed</b>",
|
||||
"invalid_pfp_count": "<b>Invalid number of profile pictures to remove</b>",
|
||||
"removed_pfps": "<b>Removed {} profile pic(s)</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.bio_enabled = False
|
||||
self.name_enabled = False
|
||||
self.pfp_enabled = False
|
||||
self.raw_bio = None
|
||||
self.raw_name = None
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
async def autopfpcmd(self, message):
|
||||
"""Rotates your profile picture every 60 seconds with x degrees, usage:
|
||||
.autopfp <degrees> <remove previous (last pfp)>
|
||||
|
||||
Degrees - 60, -10, etc
|
||||
Remove last pfp - True/1/False/0, case sensitive"""
|
||||
|
||||
if not pil_installed:
|
||||
return await utils.answer(message, self.strings("missing_pil", message))
|
||||
|
||||
if not await self.client.get_profile_photos("me", limit=1):
|
||||
return await utils.answer(message, self.strings("missing_pfp", message))
|
||||
|
||||
msg = utils.get_args(message)
|
||||
if len(msg) != 2:
|
||||
return await utils.answer(message, self.strings("invalid_args", message))
|
||||
|
||||
try:
|
||||
degrees = int(msg[0])
|
||||
except ValueError:
|
||||
return await utils.answer(message, self.strings("invalid_degrees", message))
|
||||
|
||||
try:
|
||||
delete_previous = ast.literal_eval(msg[1])
|
||||
except (ValueError, SyntaxError):
|
||||
return await utils.answer(message, self.strings("invalid_delete", message))
|
||||
|
||||
with BytesIO() as pfp:
|
||||
await self.client.download_profile_photo("me", file=pfp)
|
||||
raw_pfp = Image.open(pfp)
|
||||
|
||||
self.pfp_enabled = True
|
||||
pfp_degree = 0
|
||||
await self.allmodules.log("start_autopfp")
|
||||
await utils.answer(message, self.strings("enabled_pfp", message))
|
||||
|
||||
while self.pfp_enabled:
|
||||
pfp_degree = (pfp_degree + degrees) % 360
|
||||
rotated = raw_pfp.rotate(pfp_degree)
|
||||
with BytesIO() as buf:
|
||||
rotated.save(buf, format="JPEG")
|
||||
buf.seek(0)
|
||||
|
||||
if delete_previous:
|
||||
await self.client(
|
||||
functions.photos.DeletePhotosRequest(
|
||||
await self.client.get_profile_photos("me", limit=1)
|
||||
)
|
||||
)
|
||||
|
||||
await self.client(
|
||||
functions.photos.UploadProfilePhotoRequest(
|
||||
await self.client.upload_file(buf)
|
||||
)
|
||||
)
|
||||
buf.close()
|
||||
await asyncio.sleep(60)
|
||||
|
||||
async def stopautopfpcmd(self, message):
|
||||
"""Stop autobio cmd."""
|
||||
|
||||
if self.pfp_enabled is False:
|
||||
return await utils.answer(message, self.strings("pfp_not_enabled", message))
|
||||
self.pfp_enabled = False
|
||||
|
||||
await self.client(
|
||||
functions.photos.DeletePhotosRequest(
|
||||
await self.client.get_profile_photos("me", limit=1)
|
||||
)
|
||||
)
|
||||
await self.allmodules.log("stop_autopfp")
|
||||
await utils.answer(message, self.strings("pfp_disabled", message))
|
||||
|
||||
async def autobiocmd(self, message):
|
||||
"""Automatically changes your account's bio with current time, usage:
|
||||
.autobio '<message, time as {time}>'"""
|
||||
|
||||
msg = utils.get_args(message)
|
||||
if len(msg) != 1:
|
||||
return await utils.answer(message, self.strings("invalid_args", message))
|
||||
raw_bio = msg[0]
|
||||
if "{time}" not in raw_bio:
|
||||
return await utils.answer(message, self.strings("missing_time", message))
|
||||
|
||||
self.bio_enabled = True
|
||||
self.raw_bio = raw_bio
|
||||
await self.allmodules.log("start_autobio")
|
||||
await utils.answer(message, self.strings("enabled_bio", message))
|
||||
|
||||
while self.bio_enabled:
|
||||
current_time = time.strftime("%H:%M")
|
||||
bio = raw_bio.format(time=current_time)
|
||||
await self.client(functions.account.UpdateProfileRequest(about=bio))
|
||||
await asyncio.sleep(60)
|
||||
|
||||
async def stopautobiocmd(self, message):
|
||||
"""Stop autobio cmd."""
|
||||
|
||||
if self.bio_enabled is False:
|
||||
return await utils.answer(message, self.strings("bio_not_enabled", message))
|
||||
self.bio_enabled = False
|
||||
await self.allmodules.log("stop_autobio")
|
||||
await utils.answer(message, self.strings("disabled_bio", message))
|
||||
await self.client(
|
||||
functions.account.UpdateProfileRequest(about=self.raw_bio.format(time=""))
|
||||
)
|
||||
|
||||
async def autonamecmd(self, message):
|
||||
"""Automatically changes your Telegram name with current time, usage:
|
||||
.autoname '<message, time as {time}>'"""
|
||||
|
||||
msg = utils.get_args(message)
|
||||
if len(msg) != 1:
|
||||
return await utils.answer(message, self.strings("invalid_args", message))
|
||||
raw_name = msg[0]
|
||||
if "{time}" not in raw_name:
|
||||
return await utils.answer(message, self.strings("missing_time", message))
|
||||
|
||||
self.name_enabled = True
|
||||
self.raw_name = raw_name
|
||||
await self.allmodules.log("start_autoname")
|
||||
await utils.answer(message, self.strings("enabled_name", message))
|
||||
|
||||
while self.name_enabled:
|
||||
current_time = time.strftime("%H:%M")
|
||||
name = raw_name.format(time=current_time)
|
||||
await self.client(functions.account.UpdateProfileRequest(first_name=name))
|
||||
await asyncio.sleep(60)
|
||||
|
||||
async def stopautonamecmd(self, message):
|
||||
"""Stop autoname cmd."""
|
||||
|
||||
if self.name_enabled is False:
|
||||
return await utils.answer(
|
||||
message, self.strings("name_not_enabled", message)
|
||||
)
|
||||
self.name_enabled = False
|
||||
await self.allmodules.log("stop_autoname")
|
||||
await utils.answer(message, self.strings("disabled_name", message))
|
||||
await self.client(
|
||||
functions.account.UpdateProfileRequest(
|
||||
first_name=self.raw_name.format(time="")
|
||||
)
|
||||
)
|
||||
|
||||
async def delpfpcmd(self, message):
|
||||
"""Remove x profile pic(s) from your profile.
|
||||
.delpfp <pfps count/unlimited - remove all>"""
|
||||
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
return await utils.answer(message, self.strings("how_many_pfps", message))
|
||||
try:
|
||||
pfps_count = int(args[0])
|
||||
except ValueError:
|
||||
return await utils.answer(
|
||||
message, self.strings("invalid_pfp_count", message)
|
||||
)
|
||||
if pfps_count < 0:
|
||||
return await utils.answer(
|
||||
message, self.strings("invalid_pfp_count", message)
|
||||
)
|
||||
if pfps_count == 0:
|
||||
pfps_count = None
|
||||
|
||||
to_delete = await self.client.get_profile_photos("me", limit=pfps_count)
|
||||
await self.client(functions.photos.DeletePhotosRequest(to_delete))
|
||||
|
||||
await self.allmodules.log("delpfp")
|
||||
await utils.answer(
|
||||
message, self.strings("removed_pfps", message).format(len(to_delete))
|
||||
)
|
||||
return await utils.answer(message, self.strings("how_many_pfps", message))
|
||||
174
GeekTG/FTG-Modules/avatar.py
Normal file
174
GeekTG/FTG-Modules/avatar.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd, @dekftgmodules
|
||||
|
||||
import os
|
||||
|
||||
from telethon import functions
|
||||
from telethon.errors.rpcerrorlist import UsernameOccupiedError
|
||||
from telethon.tl.functions.account import UpdateProfileRequest, UpdateUsernameRequest
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
class GetPPMod(loader.Module):
|
||||
strings = {"name": "Profile"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
self.db = db
|
||||
|
||||
async def avacmd(self, message):
|
||||
id = utils.get_args_raw(message)
|
||||
user = await message.get_reply_message()
|
||||
chat = message.input_chat
|
||||
if user:
|
||||
photos = await self.client.get_profile_photos(user.sender)
|
||||
u = True
|
||||
else:
|
||||
photos = await self.client.get_profile_photos(chat)
|
||||
u = False
|
||||
if id.strip() == "":
|
||||
if len(photos) > 0:
|
||||
await self.client.send_file(message.chat_id, photos)
|
||||
for i in photos:
|
||||
os.remove(i)
|
||||
else:
|
||||
try:
|
||||
if u:
|
||||
photo = await self.client.download_profile_photo(user.sender)
|
||||
else:
|
||||
photo = await self.client.download_profile_photo(
|
||||
message.input_chat
|
||||
)
|
||||
await self.client.send_file(message.chat_id, photo)
|
||||
os.remove(photo)
|
||||
except Exception:
|
||||
await message.edit("<code>This user has no photos</code>")
|
||||
return
|
||||
else:
|
||||
try:
|
||||
id = int(id)
|
||||
if id <= 0:
|
||||
await message.edit("<code>ID number you entered is invalid</code>")
|
||||
return
|
||||
except Exception:
|
||||
await message.edit("<code>ID number you entered is invalid</code>")
|
||||
return
|
||||
if int(id) <= (len(photos)):
|
||||
send_photos = await self.client.download_media(photos[id - 1])
|
||||
await self.client.send_file(message.chat_id, send_photos)
|
||||
os.remove(send_photos)
|
||||
else:
|
||||
await message.edit("<code>No photo found with that id</code>")
|
||||
return
|
||||
await message.delete()
|
||||
|
||||
async def setavacmd(self, message):
|
||||
reply = await check_mediaa(message)
|
||||
if not reply:
|
||||
try:
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
return await message.edit("No reply to a media.")
|
||||
await message.edit("Downloading...")
|
||||
if reply.video:
|
||||
await message.client.download_media(reply.media, "ava.mp4")
|
||||
await message.edit("Converting...")
|
||||
os.system("ffmpeg -i ava.mp4 -c copy -an gifavaa.mp4 -y")
|
||||
os.system("ffmpeg -i gifavaa.mp4 -vf scale=360:360 gifava.mp4 -y")
|
||||
else:
|
||||
await message.client.download_media(reply.media, "tgs.tgs")
|
||||
await message.edit("Converting...")
|
||||
os.system(
|
||||
"lottie_convert.py tgs.tgs tgs.gif; mv tgs.gif gifava.mp4"
|
||||
)
|
||||
await message.edit("Uploading avatar...")
|
||||
await message.client(
|
||||
functions.photos.UploadProfilePhotoRequest(
|
||||
video=await message.client.upload_file("gifava.mp4"),
|
||||
video_start_ts=0.0,
|
||||
)
|
||||
)
|
||||
await message.edit("Uploaded.")
|
||||
os.system("rm -rf ava.mp4 gifava.mp4 gifavaa.mp4 tgs*")
|
||||
except Exception:
|
||||
await message.edit("An unexpected error occurred")
|
||||
return os.system("rm -rf ava.mp4 gifava.mp4 gifavaa.mp4 tgs*")
|
||||
else:
|
||||
reply = await message.get_reply_message()
|
||||
media = reply.photo or reply.sticker if reply else None
|
||||
if not media:
|
||||
return await message.edit("No reply on photo/sticker")
|
||||
|
||||
await message.edit("Downloading...")
|
||||
photo = await message.client.download_media(message=reply.photo)
|
||||
up = await message.client.upload_file(photo)
|
||||
await message.edit("Uploading avatar...")
|
||||
await message.client(functions.photos.UploadProfilePhotoRequest(up))
|
||||
await message.delete()
|
||||
os.remove(photo)
|
||||
|
||||
async def delavacmd(self, message):
|
||||
ava = await message.client.get_profile_photos("me", limit=1)
|
||||
if len(ava) > 0:
|
||||
await message.edit("Deleting avatar...")
|
||||
await message.client(functions.photos.DeletePhotosRequest(ava))
|
||||
await message.edit("Current avatar was deleted.")
|
||||
else:
|
||||
await message.edit("No avatar found")
|
||||
|
||||
async def delavascmd(self, message):
|
||||
ava = await message.client.get_profile_photos("me")
|
||||
if len(ava) > 0:
|
||||
await message.edit("Deleting avatars...")
|
||||
await message.client(
|
||||
functions.photos.DeletePhotosRequest(
|
||||
await message.client.get_profile_photos("me")
|
||||
)
|
||||
)
|
||||
await message.edit("All avatars was deleted")
|
||||
else:
|
||||
await message.edit("No avatar found")
|
||||
|
||||
async def setnamecmd(self, message):
|
||||
args = utils.get_args_raw(message).split("/")
|
||||
if len(args) == 1:
|
||||
firstname = args[0]
|
||||
lastname = " "
|
||||
elif len(args) == 2:
|
||||
firstname = args[0]
|
||||
lastname = args[1]
|
||||
await message.client(
|
||||
UpdateProfileRequest(first_name=firstname, last_name=lastname)
|
||||
)
|
||||
await message.edit("Name changed successfully!")
|
||||
|
||||
async def setbiocmd(self, message):
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await message.edit("No arguments.")
|
||||
await message.client(UpdateProfileRequest(about=args))
|
||||
await message.edit("Bio changed successfully!")
|
||||
|
||||
async def setusercmd(self, message):
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await message.edit("No args.")
|
||||
try:
|
||||
await message.client(UpdateUsernameRequest(args))
|
||||
await message.edit("Username changed successfully!")
|
||||
except UsernameOccupiedError:
|
||||
await message.edit("This username is already occupied!")
|
||||
|
||||
|
||||
async def check_mediaa(message):
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
return False
|
||||
if not reply.file:
|
||||
return False
|
||||
mime = reply.file.mime_type.split("/")[0].lower()
|
||||
if mime != "image":
|
||||
return False
|
||||
return reply
|
||||
278
GeekTG/FTG-Modules/banwords.py
Normal file
278
GeekTG/FTG-Modules/banwords.py
Normal file
@@ -0,0 +1,278 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @Fl1yd
|
||||
|
||||
import requests
|
||||
from telethon.tl.functions.channels import EditBannedRequest as eb
|
||||
from telethon.tl.types import ChatBannedRights as cb
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class BanWordsMod(loader.Module):
|
||||
"""Плохие слова."""
|
||||
|
||||
strings = {"name": "Ban Words"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
|
||||
async def addbwcmd(self, message):
|
||||
"""Добавить слово в список "Плохих слов". Используй: .addbw <слово>."""
|
||||
if not message.is_private:
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await message.edit("<b>Я не админ здесь.</b>")
|
||||
else:
|
||||
if not chat.admin_rights.delete_messages:
|
||||
return await message.edit("<b>У меня нет нужных прав.</b>")
|
||||
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
args = utils.get_args_raw(message).lower()
|
||||
if not args:
|
||||
return await message.edit("<b>[BanWords]</b> Нет аргументов.")
|
||||
|
||||
chat_id = str(message.chat_id)
|
||||
if chat_id not in words:
|
||||
words.setdefault(chat_id, [])
|
||||
|
||||
if "stats" not in words:
|
||||
words.update(
|
||||
{"stats": {chat_id: {"action": "none", "antimat": False, "limit": 5}}}
|
||||
)
|
||||
|
||||
if args not in words[chat_id]:
|
||||
if ", " in args:
|
||||
args = args.split(", ")
|
||||
words[chat_id].extend(args)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
await message.edit(
|
||||
f"<b>[BanWords]</b> В список чата добавлены слова - \"<code>{'; '.join(args)}</code>\"."
|
||||
)
|
||||
else:
|
||||
words[chat_id].append(args)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
await message.edit(
|
||||
f'<b>[BanWords]</b> В список чата добавлено слово - "<code>{args}</code>".'
|
||||
)
|
||||
else:
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Такое слово уже есть в списке слов чата."
|
||||
)
|
||||
|
||||
async def rmbwcmd(self, message):
|
||||
"""Удалить слово из список "Плохих слов". Используй: .rmbw <слово или all/clearall (по желанию)>.\nall - удаляет все слова из списка.\nclearall - удаляет все сохраненные данные модуля."""
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await message.edit("<b>[BanWords]</b> Нет аргументов.")
|
||||
chat_id = str(message.chat_id)
|
||||
|
||||
try:
|
||||
if args == "all":
|
||||
words.pop(chat_id)
|
||||
words["stats"].pop(chat_id)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Из списка чата удалены все слова."
|
||||
)
|
||||
|
||||
if args == "clearall":
|
||||
self.db.set("BanWords", "bws", {})
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Все списки из всех чатов были удалены."
|
||||
)
|
||||
|
||||
words[chat_id].remove(args)
|
||||
if len(words[chat_id]) == 0:
|
||||
words.pop(chat_id)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
await message.edit(
|
||||
f'<b>[BanWords]</b> Из списка чата удалено слово - "<code>{args}</code>".'
|
||||
)
|
||||
except (KeyError, ValueError):
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Этого слова нет в словаре этого чата."
|
||||
)
|
||||
|
||||
async def bwscmd(self, message):
|
||||
"""Посмотреть список "Плохих слов". Используй: .bws."""
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
chat_id = str(message.chat_id)
|
||||
|
||||
try:
|
||||
ls = words[chat_id]
|
||||
if len(ls) == 0:
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
return await message.edit("<b>[BanWords]</b> В этом чате нет списка слов.")
|
||||
|
||||
word = "".join(f"• <code>{_}</code>\n" for _ in ls)
|
||||
await message.edit(f"<b>[BanWords]</b> Список слов в этом чате:\n\n{word}")
|
||||
|
||||
async def bwstatscmd(self, message):
|
||||
"""Статистика "Плохих слов". Используй: .bwstats <clear* (по желанию)>.\n* - сбросить настройки чата."""
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
chat_id = str(message.chat_id)
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if args == "clear":
|
||||
try:
|
||||
words["stats"].pop(chat_id)
|
||||
words["stats"].update(
|
||||
{chat_id: {"antimat": False, "action": "none", "limit": 5}}
|
||||
)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit("<b>[BanWords]</b> Настройки чата сброшены.")
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Нет статистики пользователей."
|
||||
)
|
||||
|
||||
try:
|
||||
w = ""
|
||||
for _ in words["stats"][chat_id]:
|
||||
if (
|
||||
_ not in ["action", "antimat", "limit"]
|
||||
and words["stats"][chat_id][_] != 0
|
||||
):
|
||||
user = await message.client.get_entity(int(_))
|
||||
w += f'• <a href="tg://user?id={int(_)}">{user.first_name}</a>: <code>{words["stats"][chat_id][_]}</code>\n'
|
||||
return await message.edit(
|
||||
f"<b>[BanWords]</b> Кто использовал спец.слова:\n\n{w}"
|
||||
)
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> В этом чате нет тех, кто использовал спец.слова."
|
||||
)
|
||||
|
||||
async def swbwcmd(self, message):
|
||||
"""Переключить режим "Плохих слов". Используй: .swbw <режим(antimat/kick/ban/mute/none)>, или .swbw limit <кол-во:int>."""
|
||||
if not message.is_private:
|
||||
chat = await message.get_chat()
|
||||
if chat.admin_rights or chat.creator:
|
||||
if chat.admin_rights.delete_messages is False:
|
||||
return await message.edit("<b>У меня нет нужных прав.</b>")
|
||||
|
||||
else:
|
||||
return await message.edit("<b>Я не админ здесь.</b>")
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
args = utils.get_args_raw(message)
|
||||
chat_id = str(message.chat_id)
|
||||
|
||||
if chat_id not in words:
|
||||
words.setdefault(chat_id, [])
|
||||
if "stats" not in words:
|
||||
words.update(
|
||||
{"stats": {chat_id: {"action": "none", "antimat": False, "limit": 5}}}
|
||||
)
|
||||
|
||||
if not args:
|
||||
return await message.edit(
|
||||
f"<b>[BanWords]</b> Настройки чата:\n\n"
|
||||
f"<b>Лимит спец.слов:</b> {words['stats'][chat_id]['limit']}\n"
|
||||
f"<b>При достижении лимита спец.слов будет выполняться действие:</b> {words['stats'][chat_id]['action']}\n"
|
||||
f"<b>Статус режима \"антимат\":</b> {words['stats'][chat_id]['antimat']}"
|
||||
)
|
||||
if "limit" in args:
|
||||
try:
|
||||
limit = int(utils.get_args_raw(message).split(" ", 1)[1])
|
||||
words["stats"][chat_id].update({"limit": limit})
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit(
|
||||
f"<b>[BanWords]</b> Лимит спец.слов был установлен на {words['stats'][chat_id]['limit']}."
|
||||
)
|
||||
except (IndexError, ValueError):
|
||||
return await message.edit(
|
||||
f"<b>[BanWords]</b> Лимит спец.слов в этом чате - {words['stats'][chat_id]['limit']}\n"
|
||||
f"Установить новый можно командой .bwsw limit <кол-во:int>."
|
||||
)
|
||||
|
||||
if args == "antimat":
|
||||
if words["stats"][chat_id]["antimat"]:
|
||||
words["stats"][chat_id]["antimat"] = False
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit('<b>[BanWords]</b> Режим "антимат" выключен.')
|
||||
else:
|
||||
words["stats"][chat_id]["antimat"] = True
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit('<b>[BanWords]</b> Режим "антимат" включен.')
|
||||
else:
|
||||
if args == "kick":
|
||||
words["stats"][chat_id].update({"action": "kick"})
|
||||
elif args == "ban":
|
||||
words["stats"][chat_id].update({"action": "ban"})
|
||||
elif args == "mute":
|
||||
words["stats"][chat_id].update({"action": "mute"})
|
||||
elif args == "none":
|
||||
words["stats"][chat_id].update({"action": "none"})
|
||||
else:
|
||||
return await message.edit(
|
||||
"<b>[BanWords]</b> Такого режима нет в списке. Есть: kick/ban/mute/none."
|
||||
)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
return await message.edit(
|
||||
f"<b>[BanWords]</b> Теперь при достижении лимита спец.слов будет выполняться действие: {words['stats'][chat_id]['action']}."
|
||||
)
|
||||
|
||||
async def watcher(self, message):
|
||||
"""Обновление от 19.03: Фикс говнокода."""
|
||||
try:
|
||||
if message.sender_id == (await message.client.get_me()).id:
|
||||
return
|
||||
words = self.db.get("BanWords", "bws", {})
|
||||
chat_id = str(message.chat_id)
|
||||
user_id = str(message.sender_id)
|
||||
user = await message.client.get_entity(int(user_id))
|
||||
|
||||
if chat_id not in str(words):
|
||||
return
|
||||
action = words["stats"][chat_id]["action"]
|
||||
if words["stats"][chat_id]["antimat"] is True:
|
||||
r = requests.get("https://api.fl1yd.ml/badwords")
|
||||
ls = r.text.split(", ")
|
||||
else:
|
||||
ls = words[chat_id]
|
||||
|
||||
for _ in ls:
|
||||
if _.lower() in message.raw_text.lower().split():
|
||||
if user_id not in words["stats"][chat_id]:
|
||||
words["stats"][chat_id].setdefault(user_id, 0)
|
||||
|
||||
count = words["stats"][chat_id][user_id]
|
||||
words["stats"][chat_id].update({user_id: count + 1})
|
||||
self.db.set("BanWords", "bws", words)
|
||||
|
||||
if count == words["stats"][chat_id]["limit"]:
|
||||
try:
|
||||
if action == "kick":
|
||||
await message.client.kick_participant(
|
||||
int(chat_id), int(user_id)
|
||||
)
|
||||
elif action == "ban":
|
||||
await message.client(
|
||||
eb(
|
||||
int(chat_id),
|
||||
user_id,
|
||||
cb(until_date=None, view_messages=True),
|
||||
)
|
||||
)
|
||||
elif action == "mute":
|
||||
await message.client(
|
||||
eb(
|
||||
int(chat_id),
|
||||
user.id,
|
||||
cb(until_date=True, send_messages=True),
|
||||
)
|
||||
)
|
||||
words["stats"][chat_id].pop(user_id)
|
||||
self.db.set("BanWords", "bws", words)
|
||||
await message.respond(
|
||||
f"<b>[BanWords]</b> {user.first_name} достиг лимит ({words['stats'][chat_id]['limit']}) спец.слова, и был ограничен в чате."
|
||||
)
|
||||
except:
|
||||
pass
|
||||
await message.client.delete_messages(message.chat_id, message.id)
|
||||
except:
|
||||
pass
|
||||
29
GeekTG/FTG-Modules/calculator.py
Normal file
29
GeekTG/FTG-Modules/calculator.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class CalculatorMod(loader.Module):
|
||||
"""Calculator module"""
|
||||
|
||||
strings = {"name": "Calculator"}
|
||||
|
||||
async def calccmd(self, message):
|
||||
""".calc 2 * 2"""
|
||||
question = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not question:
|
||||
if not reply:
|
||||
await utils.answer(message, "<b>2+2=5</b>")
|
||||
return
|
||||
else:
|
||||
question = reply.raw_text
|
||||
try:
|
||||
answer = eval(question)
|
||||
answer = f"<b>{question}=</b><code>{answer}</code>"
|
||||
except Exception as e:
|
||||
answer = f"<b>{question}=</b><code>{e}</code>"
|
||||
await utils.answer(message, answer)
|
||||
50
GeekTG/FTG-Modules/callcontrol.py
Normal file
50
GeekTG/FTG-Modules/callcontrol.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @dekftgmodules
|
||||
|
||||
from asyncio import sleep
|
||||
|
||||
from telethon import functions
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class VGCallControllerMod(loader.Module):
|
||||
"""Control group voice calls"""
|
||||
|
||||
strings = {"name": "VGCallController"}
|
||||
|
||||
@loader.owner
|
||||
async def callstartcmd(self, m):
|
||||
"""Start call in chat"""
|
||||
if not m.chat:
|
||||
return await utils.answer(m, "<b>[VGCallController]</b> It is not a chat!")
|
||||
call = (
|
||||
await m.client(functions.channels.GetFullChannelRequest(m.chat.id))
|
||||
).full_chat.call
|
||||
if not call:
|
||||
try:
|
||||
await m.client(functions.phone.CreateGroupCallRequest(peer=m.chat))
|
||||
await utils.answer(m, "<b>[VGCallController]</b> Call started!")
|
||||
except:
|
||||
await utils.answer(m, "<b>[VGCallController]</b> Err...")
|
||||
else:
|
||||
await utils.answer(m, "<b>[VGCallController]</b> There is call now!")
|
||||
|
||||
async def callstopcmd(self, m):
|
||||
"""Stop call in chat"""
|
||||
if not m.chat:
|
||||
return await utils.answer(m, "<b>[VGCallController]</b> It is not a chat!")
|
||||
call = (
|
||||
await m.client(functions.channels.GetFullChannelRequest(m.chat.id))
|
||||
).full_chat.call
|
||||
if call:
|
||||
try:
|
||||
await m.client(functions.phone.DiscardGroupCallRequest(call))
|
||||
await utils.answer(m, "<b>[VGCallController]</b> Call stopped!")
|
||||
await sleep(5)
|
||||
except:
|
||||
await utils.answer(m, "<b>[VGCallController]</b> Err...")
|
||||
else:
|
||||
await utils.answer(m, "<b>[VGCallController]</b> There is no call now!")
|
||||
421
GeekTG/FTG-Modules/chat.py
Normal file
421
GeekTG/FTG-Modules/chat.py
Normal file
@@ -0,0 +1,421 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd, @dekftgmodules, @memeframe
|
||||
|
||||
import asyncio
|
||||
import io
|
||||
from asyncio import sleep
|
||||
from os import remove
|
||||
|
||||
from telethon import errors, functions
|
||||
from telethon.errors import (
|
||||
BotGroupsBlockedError,
|
||||
ChannelPrivateError,
|
||||
ChatAdminRequiredError,
|
||||
ChatWriteForbiddenError,
|
||||
InputUserDeactivatedError,
|
||||
MessageTooLongError,
|
||||
UserAlreadyParticipantError,
|
||||
UserBlockedError,
|
||||
UserIdInvalidError,
|
||||
UserKickedError,
|
||||
UserNotMutualContactError,
|
||||
UserPrivacyRestrictedError,
|
||||
YouBlockedUserError,
|
||||
)
|
||||
from telethon.tl.functions.channels import InviteToChannelRequest, LeaveChannelRequest
|
||||
from telethon.tl.functions.messages import AddChatUserRequest, GetCommonChatsRequest
|
||||
from telethon.tl.functions.users import GetFullUserRequest
|
||||
from telethon.tl.types import (
|
||||
ChannelParticipantCreator,
|
||||
ChannelParticipantsAdmins,
|
||||
ChannelParticipantsBots,
|
||||
)
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ChatMod(loader.Module):
|
||||
"""Чат модуль"""
|
||||
|
||||
strings = {"name": "Chat Tools"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
|
||||
async def useridcmd(self, message):
|
||||
"""Команда .userid <@ или реплай> показывает ID выбранного пользователя."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
try:
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isdigit() else args
|
||||
)
|
||||
else:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
except ValueError:
|
||||
user = await message.client.get_entity(message.sender_id)
|
||||
|
||||
await message.edit(
|
||||
f"<b>Имя:</b> <code>{user.first_name}</code>\n"
|
||||
f"<b>ID:</b> <code>{user.id}</code>"
|
||||
)
|
||||
|
||||
async def chatidcmd(self, message):
|
||||
"""Команда .chatid показывает ID чата."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
args = utils.get_args_raw(message)
|
||||
to_chat = None
|
||||
|
||||
try:
|
||||
if args:
|
||||
to_chat = int(args) if args.isdigit() else args
|
||||
else:
|
||||
to_chat = message.chat_id
|
||||
|
||||
except ValueError:
|
||||
to_chat = message.chat_id
|
||||
|
||||
chat = await message.client.get_entity(to_chat)
|
||||
|
||||
await message.edit(
|
||||
f"<b>Название:</b> <code>{chat.title}</code>\n"
|
||||
f"<b>ID</b>: <code>{chat.id}</code>"
|
||||
)
|
||||
|
||||
async def invitecmd(self, message):
|
||||
"""Используйте .invite <@ или реплай>, чтобы добавить пользователя в чат."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
|
||||
try:
|
||||
if args:
|
||||
user = int(args) if args.isdigit() else args
|
||||
else:
|
||||
user = reply.sender_id
|
||||
|
||||
user = await message.client.get_entity(user)
|
||||
|
||||
if not message.is_channel and message.is_group:
|
||||
await message.client(
|
||||
AddChatUserRequest(
|
||||
chat_id=message.chat_id, user_id=user.id, fwd_limit=1000000
|
||||
)
|
||||
)
|
||||
else:
|
||||
await message.client(
|
||||
InviteToChannelRequest(channel=message.chat_id, users=[user.id])
|
||||
)
|
||||
return await message.edit("<b>Пользователь приглашён успешно!</b>")
|
||||
|
||||
except ValueError:
|
||||
m = "<b>Неверный @ или ID.</b>"
|
||||
except UserIdInvalidError:
|
||||
m = "<b>Неверный @ или ID.</b>"
|
||||
except UserPrivacyRestrictedError:
|
||||
m = "<b>Настройки приватности пользователя не позволяют пригласить его.</b>"
|
||||
except UserNotMutualContactError:
|
||||
m = "<b>Настройки приватности пользователя не позволяют пригласить его.</b>"
|
||||
except ChatAdminRequiredError:
|
||||
m = "<b>У меня нет прав.</b>"
|
||||
except ChatWriteForbiddenError:
|
||||
m = "<b>У меня нет прав.</b>"
|
||||
except ChannelPrivateError:
|
||||
m = "<b>У меня нет прав.</b>"
|
||||
except UserKickedError:
|
||||
m = "<b>Пользователь кикнут из чата, обратитесь к администраторам.</b>"
|
||||
except BotGroupsBlockedError:
|
||||
m = "<b>Бот заблокирован в чате, обратитесь к администраторам.</b>"
|
||||
except UserBlockedError:
|
||||
m = "<b>Пользователь заблокирован в чате, обратитесь к администраторам.</b>"
|
||||
except InputUserDeactivatedError:
|
||||
m = "<b>Аккаунт пользователя удалён.</b>"
|
||||
except UserAlreadyParticipantError:
|
||||
m = "<b>Пользователь уже в группе.</b>"
|
||||
except YouBlockedUserError:
|
||||
m = "<b>Вы заблокировали этого пользователя.</b>"
|
||||
return await message.reply(m)
|
||||
|
||||
async def leavecmd(self, message):
|
||||
"""Используйте команду .leave, чтобы кикнуть себя из чата."""
|
||||
args = utils.get_args_raw(message)
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
if args:
|
||||
await message.edit(f"<b>До связи.\nПричина: {args}</b>")
|
||||
else:
|
||||
await message.edit("<b>До связи.</b>")
|
||||
await message.client(LeaveChannelRequest(message.chat_id))
|
||||
|
||||
async def userscmd(self, message):
|
||||
"""Команда .users <имя>; ничего выводит список всех пользователей в чате."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
await message.edit("<b>Считаем...</b>")
|
||||
args = utils.get_args_raw(message)
|
||||
info = await message.client.get_entity(message.chat_id)
|
||||
title = info.title or "этом чате"
|
||||
|
||||
if args:
|
||||
users = await message.client.get_participants(
|
||||
message.chat_id, search=f"{args}"
|
||||
)
|
||||
mentions = f'<b>В чате "{title}" найдено {len(users)} пользователей с именем {args}:</b> \n'
|
||||
|
||||
else:
|
||||
users = await message.client.get_participants(message.chat_id)
|
||||
mentions = f'<b>Пользователей в "{title}": {len(users)}</b> \n'
|
||||
for user in users:
|
||||
if user.deleted:
|
||||
mentions += f"\n• Удалённый аккаунт <b>|</b> <code>{user.id}</code>"
|
||||
|
||||
else:
|
||||
mentions += f'\n• <a href ="tg://user?id={user.id}">{user.first_name}</a> | <code>{user.id}</code>'
|
||||
try:
|
||||
await message.edit(mentions)
|
||||
except MessageTooLongError:
|
||||
await message.edit(
|
||||
"<b>Черт, слишком большой чат. Загружаю список пользователей в файл...</b>"
|
||||
)
|
||||
with open("userslist.md", "w+") as file:
|
||||
file.write(mentions)
|
||||
await message.client.send_file(
|
||||
message.chat_id,
|
||||
"userslist.md",
|
||||
caption=f"<b>Пользователей в {title}:</b>",
|
||||
reply_to=message.id,
|
||||
)
|
||||
|
||||
remove("userslist.md")
|
||||
await message.delete()
|
||||
|
||||
async def adminscmd(self, message):
|
||||
"""Команда .admins показывает список всех админов в чате."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
await message.edit("<b>Считаем...</b>")
|
||||
info = await message.client.get_entity(message.chat_id)
|
||||
title = info.title or "this chat"
|
||||
|
||||
admins = await message.client.get_participants(
|
||||
message.chat_id, filter=ChannelParticipantsAdmins
|
||||
)
|
||||
mentions = f'<b>Админов в "{title}": {len(admins)}</b>\n'
|
||||
|
||||
for user in admins:
|
||||
admin = admins[
|
||||
admins.index((await message.client.get_entity(user.id)))
|
||||
].participant
|
||||
if admin:
|
||||
rank = admin.rank or "admin"
|
||||
|
||||
else:
|
||||
rank = (
|
||||
"creator" if type(admin) == ChannelParticipantCreator else "admin"
|
||||
)
|
||||
if user.deleted:
|
||||
mentions += f"\n• Удалённый аккаунт <b>|</b> <code>{user.id}</code>"
|
||||
|
||||
else:
|
||||
mentions += f'\n• <a href="tg://user?id={user.id}">{user.first_name}</a> | {rank} | <code>{user.id}</code>'
|
||||
try:
|
||||
await message.edit(mentions)
|
||||
except MessageTooLongError:
|
||||
await message.edit(
|
||||
"Черт, слишком много админов здесь. Загружаю список админов в файл..."
|
||||
)
|
||||
with open("adminlist.md", "w+") as file:
|
||||
file.write(mentions)
|
||||
await message.client.send_file(
|
||||
message.chat_id,
|
||||
"adminlist.md",
|
||||
caption=f'<b>Админов в "{title}":<b>',
|
||||
reply_to=message.id,
|
||||
)
|
||||
|
||||
remove("adminlist.md")
|
||||
await message.delete()
|
||||
|
||||
async def botscmd(self, message):
|
||||
"""Команда .bots показывает список всех ботов в чате."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
await message.edit("<b>Считаем...</b>")
|
||||
|
||||
info = await message.client.get_entity(message.chat_id)
|
||||
title = info.title or "this chat"
|
||||
|
||||
bots = await message.client.get_participants(
|
||||
message.to_id, filter=ChannelParticipantsBots
|
||||
)
|
||||
mentions = f'<b>Ботов в "{title}": {len(bots)}</b>\n'
|
||||
|
||||
for user in bots:
|
||||
mentions += (
|
||||
f"\n• Удалённый бот <b>|</b> <code>{user.id}</code> "
|
||||
if user.deleted
|
||||
else f'\n• <a href="tg://user?id={user.id}">{user.first_name}</a> | <code>{user.id}</code>'
|
||||
)
|
||||
|
||||
try:
|
||||
await message.edit(mentions, parse_mode="html")
|
||||
except MessageTooLongError:
|
||||
await message.edit(
|
||||
"Черт, слишком много ботов здесь. Загружаю " "список ботов в файл..."
|
||||
)
|
||||
with open("botlist.md", "w+") as file:
|
||||
file.write(mentions)
|
||||
await message.client.send_file(
|
||||
message.chat_id,
|
||||
"botlist.md",
|
||||
caption=f'<b>Ботов в "{title}":</b>',
|
||||
reply_to=message.id,
|
||||
)
|
||||
|
||||
remove("botlist.md")
|
||||
await message.delete()
|
||||
|
||||
async def commoncmd(self, message):
|
||||
"""Используй .common <@ или реплай>, чтобы узнать общие чаты с
|
||||
пользователем."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
await message.edit("<b>Считаем...</b>")
|
||||
try:
|
||||
if args:
|
||||
if args.isnumeric():
|
||||
user = int(args)
|
||||
user = await message.client.get_entity(user)
|
||||
else:
|
||||
user = await message.client.get_entity(args)
|
||||
else:
|
||||
user = await utils.get_user(reply)
|
||||
except ValueError:
|
||||
return await message.edit("<b>Не удалось найти пользователя.</b>")
|
||||
msg = f"<b>Общие чаты с {user.first_name}:</b>\n"
|
||||
user = await message.client(GetFullUserRequest(user.id))
|
||||
comm = await message.client(
|
||||
GetCommonChatsRequest(user_id=user.user.id, max_id=0, limit=100)
|
||||
)
|
||||
count = 0
|
||||
m = ""
|
||||
for chat in comm.chats:
|
||||
m += f'\n• <a href="tg://resolve?domain={chat.username}">{chat.title}</a> <b>|</b> <code>{chat.id}</code> '
|
||||
count += 1
|
||||
msg = f"<b>Общие чаты с {user.user.first_name}: {count}</b>\n"
|
||||
await message.edit(f"{msg} {m}")
|
||||
|
||||
async def chatdumpcmd(self, message):
|
||||
""".chatdump <n> <m> <s>
|
||||
Дамп юзеров чата
|
||||
<n> - Получить только пользователей с открытыми номерами
|
||||
<m> - Отправить дамп в избранное
|
||||
<s> - Тихий дамп
|
||||
"""
|
||||
if not message.chat:
|
||||
await message.edit("<b>Это не чат</b>")
|
||||
return
|
||||
chat = message.chat
|
||||
num = False
|
||||
silent = False
|
||||
tome = False
|
||||
if utils.get_args_raw(message):
|
||||
a = utils.get_args_raw(message)
|
||||
if "n" in a:
|
||||
num = True
|
||||
if "s" in a:
|
||||
silent = True
|
||||
if "m" in a:
|
||||
tome = True
|
||||
if not silent:
|
||||
await message.edit("🖤Дампим чат...🖤")
|
||||
else:
|
||||
await message.delete()
|
||||
f = io.BytesIO()
|
||||
f.name = f"Dump by {chat.id}.csv"
|
||||
f.write("FNAME;LNAME;USER;ID;NUMBER\n".encode())
|
||||
me = await message.client.get_me()
|
||||
for i in await message.client.get_participants(message.to_id):
|
||||
if i.id == me.id:
|
||||
continue
|
||||
if num and i.phone or not num:
|
||||
f.write(
|
||||
f"{str(i.first_name)};{str(i.last_name)};{str(i.username)};{str(i.id)};{str(i.phone)}\n".encode()
|
||||
)
|
||||
f.seek(0)
|
||||
if tome:
|
||||
await message.client.send_file("me", f, caption="Дамп чата " + str(chat.id))
|
||||
else:
|
||||
await message.client.send_file(
|
||||
message.to_id, f, caption=f"Дамп чата {str(chat.id)}"
|
||||
)
|
||||
|
||||
if not silent:
|
||||
if tome:
|
||||
if num:
|
||||
await message.edit("🖤Дамп юзеров чата сохранён в " "избранных!🖤")
|
||||
else:
|
||||
await message.edit(
|
||||
"🖤Дамп юзеров чата с открытыми "
|
||||
"номерами сохранён в избранных!🖤"
|
||||
)
|
||||
else:
|
||||
await message.delete()
|
||||
f.close()
|
||||
|
||||
async def adduserscmd(self, event):
|
||||
"""Add members"""
|
||||
if len(event.text.split()) == 2:
|
||||
idschannelgroup = event.text.split(" ", maxsplit=1)[1]
|
||||
user = [
|
||||
i async for i in event.client.iter_participants(event.to_id.channel_id)
|
||||
]
|
||||
await event.edit(
|
||||
f"<b>{len(user)} пользователей будет приглашено из чата {event.to_id.channel_id} в чат/канал {idschannelgroup}</b>"
|
||||
)
|
||||
for u in user:
|
||||
try:
|
||||
try:
|
||||
if not u.bot:
|
||||
await event.client(
|
||||
functions.channels.InviteToChannelRequest(
|
||||
idschannelgroup, [u.id]
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(1)
|
||||
except:
|
||||
pass
|
||||
except errors.FloodWaitError as e:
|
||||
print("Flood for", e.seconds)
|
||||
else:
|
||||
await event.edit("<b>Куда приглашать будем?</b>")
|
||||
|
||||
async def reportcmd(self, message):
|
||||
"""Репорт пользователя за спам."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
return await message.edit("<b>Кого я должен зарепортить?</b>")
|
||||
|
||||
await message.client(functions.messages.ReportSpamRequest(peer=user.id))
|
||||
await message.edit("<b>Ты получил репорт за спам!</b>")
|
||||
await sleep(1)
|
||||
await message.delete()
|
||||
223
GeekTG/FTG-Modules/chatvoicemod.py
Normal file
223
GeekTG/FTG-Modules/chatvoicemod.py
Normal file
@@ -0,0 +1,223 @@
|
||||
# .------.------.------.------.------.------.------.------.------.------.
|
||||
# |D.--. |4.--. |N.--. |1.--. |3.--. |L.--. |3.--. |K.--. |0.--. |0.--. |
|
||||
# | :/\: | :/\: | :(): | :/\: | :(): | :/\: | :(): | :/\: | :/\: | :/\: |
|
||||
# | (__) | :\/: | ()() | (__) | ()() | (__) | ()() | :\/: | :\/: | :\/: |
|
||||
# | '--'D| '--'4| '--'N| '--'1| '--'3| '--'L| '--'3| '--'K| '--'0| '--'0|
|
||||
# `------`------`------`------`------`------`------`------`------`------'
|
||||
#
|
||||
# Copyright 2022 t.me/D4n13l3k00
|
||||
# Licensed under the Creative Commons CC BY-NC-ND 4.0
|
||||
#
|
||||
# Full license text can be found at:
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
||||
#
|
||||
# Human-friendly one:
|
||||
# https://creativecommons.org/licenses/by-nc-nd/4.0
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
from typing import *
|
||||
|
||||
import pytgcalls
|
||||
import youtube_dl
|
||||
from pytgcalls import PyTgCalls, StreamType
|
||||
from pytgcalls.types.input_stream import AudioPiped, AudioVideoPiped
|
||||
from pytgcalls.types.input_stream.quality import HighQualityAudio, HighQualityVideo
|
||||
from telethon import types
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
# meta developer: @D4n13l3k00
|
||||
# requires: py-tgcalls youtube-dl
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ChatVoiceMod(loader.Module):
|
||||
"""Module for working with voicechat"""
|
||||
|
||||
strings = {
|
||||
"name": "ChatVoiceMod",
|
||||
"downloading": "<b>[ChatVoiceMod]</b> Downloading...",
|
||||
"playing": "<b>[ChatVoiceMod]</b> Playing...",
|
||||
"notjoined": "<b>[ChatVoiceMod]</b> You are not joined",
|
||||
"stop": "<b>[ChatVoiceMod]</b> Playing stopped!",
|
||||
"leave": "<b>[ChatVoiceMod]</b> Leaved!",
|
||||
"pause": "<b>[ChatVoiceMod]</b> Paused!",
|
||||
"resume": "<b>[ChatVoiceMod]</b> Resumed!",
|
||||
"mute": "<b>[ChatVoiceMod]</b> Muted!",
|
||||
"unmute": "<b>[ChatVoiceMod]</b> Unmuted!",
|
||||
"error": "<b>[ChatVoiceMod]</b> Error: <code>{}</code>",
|
||||
"noargs": "<b>[ChatVoiceMod]</b> No args",
|
||||
"noreply": "<b>[ChatVoiceMod]</b> No reply",
|
||||
"nofile": "<b>[ChatVoiceMod]</b> No file",
|
||||
"nofiles": "<b>[ChatVoiceMod]</b> No files",
|
||||
"deleted": "<b>[ChatVoiceMod]</b> <code>{}</code> successfully deleted",
|
||||
"downloaded": "<b>[ChatVoiceMod]</b> Downloaded to <code>dl/{0}</code>. For playing use:\n<code>.cplaya dl/{0}</code>\n<code>.cplayv dl/{0}</code>",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, _):
|
||||
self.client = client
|
||||
self.call = PyTgCalls(client)
|
||||
|
||||
@self.call.on_stream_end()
|
||||
async def _(_, update):
|
||||
with contextlib.suppress(Exception):
|
||||
await self.call.leave_group_call(update.chat_id)
|
||||
|
||||
await self.call.start()
|
||||
|
||||
async def parse_args(self, args):
|
||||
if not args or not re.match(
|
||||
r"http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?",
|
||||
args,
|
||||
):
|
||||
return args
|
||||
with youtube_dl.YoutubeDL({"format": "best"}) as ydl:
|
||||
info = ydl.extract_info(args, download=False)
|
||||
return info["formats"][0]["url"]
|
||||
|
||||
async def cdlcmd(self, m: types.Message):
|
||||
"<reply_to_media> <name: optional> - Download media to server in `dl` folder"
|
||||
args = utils.get_args_raw(m)
|
||||
reply = await m.get_reply_message()
|
||||
if not reply:
|
||||
return await utils.answer(m, self.strings("noreply"))
|
||||
name = args or reply.file.name
|
||||
try:
|
||||
m = await utils.answer(m, self.strings("downloading"))
|
||||
await reply.download_media(f"dl/{name}")
|
||||
await utils.answer(m, self.strings("downloaded").format(name))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def clscmd(self, m: types.Message):
|
||||
"List all files in `dl` folder"
|
||||
if not os.path.isdir("dl") or not os.listdir("dl"):
|
||||
return await utils.answer(m, self.strings("nofiles"))
|
||||
files = [f"<code>dl/{f}</code>" for f in os.listdir("dl")]
|
||||
await utils.answer(m, "\n".join(files))
|
||||
|
||||
# command for deleting file from dl folder
|
||||
async def cdelcmd(self, m: types.Message):
|
||||
"<name> - Delete file from `dl` folder"
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
return await utils.answer(m, self.strings("noargs"))
|
||||
if not args.startswith("dl/"):
|
||||
args = f"dl/{args}"
|
||||
if not os.path.isfile(f"{args}"):
|
||||
return await utils.answer(m, self.strings("nofile"))
|
||||
try:
|
||||
os.remove(f"{args}")
|
||||
await utils.answer(m, self.strings("deleted").format(args))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cplayvcmd(self, m: types.Message):
|
||||
"<link/path/reply_to_video> - Play video in voice chat"
|
||||
try:
|
||||
reply = await m.get_reply_message()
|
||||
path = await self.parse_args(utils.get_args_raw(m))
|
||||
chat = m.chat.id
|
||||
if not path:
|
||||
if not reply:
|
||||
return await utils.answer(m, self.strings("noargs"))
|
||||
m = await utils.answer(m, self.strings("downloading"))
|
||||
path = await reply.download_media()
|
||||
with contextlib.suppress(pytgcalls.exceptions.GroupCallNotFound):
|
||||
self.call.get_active_call(chat)
|
||||
await self.call.leave_group_call(chat)
|
||||
await self.call.join_group_call(
|
||||
chat,
|
||||
AudioVideoPiped(
|
||||
path,
|
||||
HighQualityAudio(),
|
||||
HighQualityVideo(),
|
||||
),
|
||||
stream_type=StreamType().pulse_stream,
|
||||
)
|
||||
await utils.answer(m, self.strings("playing"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cplayacmd(self, m: types.Message):
|
||||
"<link/path/reply_to_audio> - Play audio in voice chat"
|
||||
try:
|
||||
reply = await m.get_reply_message()
|
||||
path = await self.parse_args(utils.get_args_raw(m))
|
||||
chat = m.chat.id
|
||||
if not path:
|
||||
if not reply:
|
||||
return await utils.answer(m, self.strings("noargs"))
|
||||
m = await utils.answer(m, self.strings("downloading"))
|
||||
path = await reply.download_media()
|
||||
with contextlib.suppress(pytgcalls.exceptions.GroupCallNotFound):
|
||||
self.call.get_active_call(chat)
|
||||
await self.call.leave_group_call(chat)
|
||||
await self.call.join_group_call(
|
||||
chat,
|
||||
AudioPiped(
|
||||
path,
|
||||
HighQualityAudio(),
|
||||
),
|
||||
stream_type=StreamType().pulse_stream,
|
||||
)
|
||||
await utils.answer(m, self.strings("playing"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cleavecmd(self, m: types.Message):
|
||||
"Leave"
|
||||
try:
|
||||
self.call.get_active_call(m.chat.id)
|
||||
await self.call.leave_group_call(m.chat.id)
|
||||
await utils.answer(m, self.strings("leave"))
|
||||
except pytgcalls.exceptions.GroupCallNotFound:
|
||||
await utils.answer(m, self.strings("notjoined"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cmutecmd(self, m: types.Message):
|
||||
"Mute"
|
||||
try:
|
||||
self.call.get_active_call(m.chat.id)
|
||||
await self.call.mute_stream(m.chat.id)
|
||||
await utils.answer(m, self.strings("mute"))
|
||||
except pytgcalls.exceptions.GroupCallNotFound:
|
||||
await utils.answer(m, self.strings("notjoined"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cunmutecmd(self, m: types.Message):
|
||||
"Unmute"
|
||||
try:
|
||||
self.call.get_active_call(m.chat.id)
|
||||
await self.call.unmute_stream(m.chat.id)
|
||||
await utils.answer(m, self.strings("unmute"))
|
||||
except pytgcalls.exceptions.GroupCallNotFound:
|
||||
await utils.answer(m, self.strings("notjoined"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cpausecmd(self, m: types.Message):
|
||||
"Pause"
|
||||
try:
|
||||
self.call.get_active_call(m.chat.id)
|
||||
await self.call.pause_stream(m.chat.id)
|
||||
await utils.answer(m, self.strings("pause"))
|
||||
except pytgcalls.exceptions.GroupCallNotFound:
|
||||
await utils.answer(m, self.strings("notjoined"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
|
||||
async def cresumecmd(self, m: types.Message):
|
||||
"Resume"
|
||||
try:
|
||||
self.call.get_active_call(m.chat.id)
|
||||
await self.call.resume_stream(m.chat.id)
|
||||
await utils.answer(m, self.strings("resume"))
|
||||
except pytgcalls.exceptions.GroupCallNotFound:
|
||||
await utils.answer(m, self.strings("notjoined"))
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings("error").format(str(e)))
|
||||
136
GeekTG/FTG-Modules/conthelper.py
Normal file
136
GeekTG/FTG-Modules/conthelper.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @Yahikor0
|
||||
|
||||
from telethon import functions
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ConthelperMod(loader.Module):
|
||||
"""
|
||||
Commands:
|
||||
"""
|
||||
|
||||
strings = {
|
||||
"name": "Conthelper",
|
||||
"blocked": "<b>{} was blacklisted.</b>",
|
||||
"unblocked": "<b>{} removed from the blacklist.</b>",
|
||||
"delcontact": "<b>{} was removed from contacts.</b>",
|
||||
"who_to_block": "<b>Indicate, who to block.</b>",
|
||||
"who_to_unblock": "<b>Indicate, who to unblock.</b>",
|
||||
"who_to_delcontact": "<b>Indicate, who to remove from contacts.</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.me = None
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
self.client = client
|
||||
self.me = await client.get_me(True)
|
||||
|
||||
async def reportcmd(self, message):
|
||||
"""User report for spam."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if message.chat_id != (await message.client.get_me()).id and message.is_private:
|
||||
user = await message.client.get_entity(message.chat_id)
|
||||
else:
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
return await message.edit("<b>Who I must report?</b>")
|
||||
|
||||
await message.client(functions.messages.ReportSpamRequest(peer=user.id))
|
||||
await message.edit("<b>You get report for spam!</b>")
|
||||
|
||||
async def blockcmd(self, message):
|
||||
"""Use: .block to block this user."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if message.chat_id != (await message.client.get_me()).id and message.is_private:
|
||||
user = await message.client.get_entity(message.chat_id)
|
||||
else:
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
if not user:
|
||||
await utils.answer(message, self.strings["who_to_block"])
|
||||
return
|
||||
await message.client(functions.contacts.BlockRequest(user))
|
||||
await utils.answer(message, self.strings["blocked"].format(user.first_name))
|
||||
|
||||
async def unblockcmd(self, message):
|
||||
"""Use: .unblock to unblock this user."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if message.chat_id != (await message.client.get_me()).id and message.is_private:
|
||||
user = await message.client.get_entity(message.chat_id)
|
||||
else:
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
if not user:
|
||||
await utils.answer(message, self.strings["who_to_unblock"])
|
||||
return
|
||||
await message.client(functions.contacts.UnblockRequest(user))
|
||||
await utils.answer(message, self.strings["unblocked"].format(user.first_name))
|
||||
|
||||
async def delcontcmd(self, message):
|
||||
"""Use: .delcont to remove a user from contacts."""
|
||||
args = utils.get_args(message)
|
||||
reply = await message.get_reply_message()
|
||||
if message.chat_id != (await message.client.get_me()).id and message.is_private:
|
||||
user = await message.client.get_entity(message.chat_id)
|
||||
else:
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
if not user:
|
||||
await utils.answer(message, self.strings["who_to_delcontact"])
|
||||
return
|
||||
await message.client(functions.contacts.DeleteContactsRequest(id=[user.id]))
|
||||
await utils.answer(message, self.strings["delcontact"].format(user.first_name))
|
||||
|
||||
async def addcontcmd(self, message):
|
||||
"""Use: .addcont to add somebody in contacts."""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not args:
|
||||
return await message.edit("<b>Where args?.</b>")
|
||||
if not reply:
|
||||
return await message.edit("<b>Where reply?</b>")
|
||||
else:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
try:
|
||||
await message.client(
|
||||
functions.contacts.AddContactRequest(
|
||||
id=user.id,
|
||||
first_name=args,
|
||||
last_name=" ",
|
||||
phone="phone",
|
||||
add_phone_privacy_exception=False,
|
||||
)
|
||||
)
|
||||
await message.edit(
|
||||
f"<code>{user.id}</code> added to contacts <code>{args}</code>"
|
||||
)
|
||||
except:
|
||||
return await message.edit(
|
||||
"<b>Something went wrong (come up with different reasons).</b>"
|
||||
)
|
||||
608
GeekTG/FTG-Modules/demot.py
Normal file
608
GeekTG/FTG-Modules/demot.py
Normal file
@@ -0,0 +1,608 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules, @ftgmodulesbyfl1yd, @D4n13l3k00
|
||||
|
||||
# requires: Pillow
|
||||
|
||||
import io
|
||||
import textwrap
|
||||
from asyncio.exceptions import CancelledError, TimeoutError
|
||||
from textwrap import wrap
|
||||
|
||||
import requests
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageOps
|
||||
from telethon import events, functions
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
from telethon.events import NewMessage
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
CHAT = "@demotilifebot"
|
||||
|
||||
|
||||
@loader.tds
|
||||
class DemotivatorMod(loader.Module):
|
||||
"""Deotivators"""
|
||||
|
||||
strings = {"name": "Demotivator"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
@loader.owner
|
||||
async def demoticmd(self, message):
|
||||
"""Demotiving photo without compression"""
|
||||
await cmds(message, 0)
|
||||
|
||||
async def demotcmd(self, message):
|
||||
"""Demotiving photo"""
|
||||
await cmds(message, 1)
|
||||
|
||||
async def bottomcmd(self, message):
|
||||
"""Imposes text in the photo from below"""
|
||||
return await procces_img(message, 1)
|
||||
|
||||
async def topcmd(self, message):
|
||||
"""Imposes text on the photo at the top"""
|
||||
return await procces_img(message, 2)
|
||||
|
||||
async def centercmd(self, message):
|
||||
"""Imposes text on the photo at the center"""
|
||||
return await procces_img(message, 3)
|
||||
|
||||
async def demotirandcmd(self, message):
|
||||
"""Random demotiving photo without compression"""
|
||||
await cmdrands(message, 0)
|
||||
|
||||
async def demotrandcmd(self, message):
|
||||
"""Random demotiving photo"""
|
||||
await cmdrands(message, 1)
|
||||
|
||||
async def nqcmd(self, message):
|
||||
"""Quotes from the message"""
|
||||
chat = "@ShittyQuoteBot"
|
||||
text = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not text and not reply:
|
||||
await message.edit("<b>No reply</b>")
|
||||
return
|
||||
await message.edit("<b>Demotivating...</b>")
|
||||
async with message.client.conversation(chat) as conv:
|
||||
if text:
|
||||
try:
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1389323591)
|
||||
)
|
||||
await message.client.send_message(chat, text)
|
||||
response = await response
|
||||
except YouBlockedUserError:
|
||||
return await message.edit("<b>Unblock @ShittyQuoteBot</b>")
|
||||
else:
|
||||
try:
|
||||
user = await utils.get_user(reply)
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1389323591)
|
||||
)
|
||||
await message.client.send_message(
|
||||
chat, f"{reply.raw_text} (с) {user.first_name}"
|
||||
)
|
||||
response = await response
|
||||
except YouBlockedUserError:
|
||||
return await message.edit("<b>Unblock @ShittyQuoteBot</b>")
|
||||
if response.text:
|
||||
await message.client.send_message(message.to_id, f"<b> {response.text}</b>")
|
||||
await message.delete()
|
||||
if response.media:
|
||||
await message.client.send_file(
|
||||
message.to_id, response.media, reply_to=reply.id if reply else None
|
||||
)
|
||||
await message.delete()
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer="ShittyQuoteBot", max_id=0, just_clear=False, revoke=True
|
||||
)
|
||||
)
|
||||
|
||||
async def mqcmd(self, message):
|
||||
"""Quotes from the message 2"""
|
||||
bw = not utils.get_args(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not reply or not reply.raw_text:
|
||||
return await message.edit("<b>Reply to message!</b>")
|
||||
|
||||
sender = reply.sender_id
|
||||
|
||||
if not sender:
|
||||
sender = message.chat.id
|
||||
if sender == 1087968824:
|
||||
sender = message.chat.id
|
||||
pfp = await message.client.download_profile_photo(sender, bytes)
|
||||
await message.edit("<b>Demotivating...</b>")
|
||||
if not pfp:
|
||||
pfp = b"BM:\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x04\x00\x00\x00\xc4\x0e\x00\x00\xc4\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00"
|
||||
text = "\n".join(wrap(reply.raw_text, 30))
|
||||
text = "“" + text + "„"
|
||||
bf = requests.get(
|
||||
"https://raw.githubusercontent.com/KeyZenD/l/master/times.ttf"
|
||||
).content
|
||||
font = ImageFont.truetype(io.BytesIO(bf), 50)
|
||||
im = Image.open(io.BytesIO(pfp))
|
||||
if bw:
|
||||
im = im.convert("L")
|
||||
im = im.convert("RGBA").resize((1024, 1024))
|
||||
w, h = im.size
|
||||
w_, h_ = 20 * (w // 100), 20 * (h // 100)
|
||||
im_ = Image.new("RGBA", (w - w_, h - h_), (0, 0, 0))
|
||||
im_.putalpha(150)
|
||||
im.paste(im_, (w_ // 2, h_ // 2), im_)
|
||||
draw = ImageDraw.Draw(im)
|
||||
_w, _h = draw.textsize(text=text, font=font)
|
||||
x, y = (w - _w) // 2, (h - _h) // 2
|
||||
draw.text((x, y), text=text, font=font, fill="#fff", align="center")
|
||||
output = io.BytesIO()
|
||||
im.save(output, "PNG")
|
||||
output.seek(0)
|
||||
await reply.reply(file=output)
|
||||
await message.delete()
|
||||
|
||||
|
||||
async def cmds(message, type_):
|
||||
event, is_reply = await check_media(message)
|
||||
if not event:
|
||||
if message.client._conversations.get(1376531590) is not None:
|
||||
return await message.edit("<b>Please wait.</b>")
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
if (
|
||||
not reply
|
||||
or not reply.media
|
||||
or not any(
|
||||
True
|
||||
for _ in ("sticker", "photo", "video", "video_note", "animation")
|
||||
if getattr(reply, _, None) is not None
|
||||
)
|
||||
):
|
||||
return await message.edit("<b>Reply to photo/video/sticker/gif</b>")
|
||||
if reply.file.size > 4194304:
|
||||
return await message.edit("<b>Video only up to 4mb</b>")
|
||||
args = utils.get_args_raw(message) or reply.message
|
||||
if not args:
|
||||
return await message.edit("<b>No text</b>")
|
||||
if len(args) > 500:
|
||||
return await message.edit("<b>Text only up to 500 symbols</b>")
|
||||
|
||||
await message.edit("<b>Demotivating...</b>")
|
||||
async with message.client.conversation(CHAT, timeout=160) as conv:
|
||||
try:
|
||||
response = conv.wait_event(NewMessage(incoming=True, from_users=CHAT))
|
||||
msg = await reply.forward_to(CHAT)
|
||||
await msg.reply(f"/demoti {args}")
|
||||
response = await response
|
||||
if not response.media:
|
||||
if response.raw_text.startswith("[400]"):
|
||||
return await message.edit("<b>Please wait 10 sec</b>")
|
||||
response = await conv.wait_event(
|
||||
NewMessage(incoming=True, from_users=CHAT)
|
||||
)
|
||||
|
||||
except YouBlockedUserError:
|
||||
return await message.edit(f"<b>Unblock {CHAT}</b>")
|
||||
|
||||
except (TimeoutError, CancelledError):
|
||||
return await message.edit("<b>Bot isn`t responding</b>")
|
||||
|
||||
if response.media is None:
|
||||
return await message.edit("<b>Error</b>")
|
||||
|
||||
await message.client.send_file(
|
||||
message.to_id, response.media, reply_to=reply
|
||||
)
|
||||
await message.delete()
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer=CHAT, max_id=0, just_clear=False, revoke=True
|
||||
)
|
||||
)
|
||||
return
|
||||
text = utils.get_args_raw(message)
|
||||
|
||||
if not text:
|
||||
await message.edit("<b>Reply to photo with text</b>")
|
||||
return
|
||||
await message.edit("Demotivating...")
|
||||
bytes_image = await event.download_media(bytes)
|
||||
demotivator = await demotion(font_bytes, bytes_image, text, type_)
|
||||
await message.edit("Sending...")
|
||||
if is_reply:
|
||||
await message.delete()
|
||||
return await event.reply(file=demotivator)
|
||||
return await event.edit(file=demotivator, text="")
|
||||
|
||||
|
||||
async def cmdrands(message, type_):
|
||||
event, is_reply = await check_media(message)
|
||||
if not event:
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.edit("<b>Reply to media</b>")
|
||||
return
|
||||
try:
|
||||
media = reply.media
|
||||
except Exception:
|
||||
return await message.edit("<b>Only media</b>")
|
||||
|
||||
chat = "@super_rjaka_demotivator_bot"
|
||||
await message.edit("<b>Demotivating...</b>")
|
||||
async with message.client.conversation(chat) as conv:
|
||||
try:
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1016409811)
|
||||
)
|
||||
mm = await message.client.send_file(chat, media, caption=args)
|
||||
response = await response
|
||||
await mm.delete()
|
||||
except YouBlockedUserError:
|
||||
await message.reply("<b>Разблокируй @super_rjaka_demotivator_bot</b>")
|
||||
return
|
||||
await message.edit("<b>Sending...</b>")
|
||||
await message.delete()
|
||||
await response.delete()
|
||||
await message.client.send_file(
|
||||
message.to_id,
|
||||
response.media,
|
||||
reply_to=await message.get_reply_message(),
|
||||
)
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer="super_rjaka_demotivator_bot",
|
||||
max_id=0,
|
||||
just_clear=False,
|
||||
revoke=True,
|
||||
)
|
||||
)
|
||||
text = utils.get_args_raw(message)
|
||||
|
||||
if not text:
|
||||
await message.edit("<b>Reply to photo with text</b>")
|
||||
return
|
||||
await message.edit("<b>Demotivating...</b>")
|
||||
bytes_image = await event.download_media(bytes)
|
||||
demotivator = await demotion(font_bytes, bytes_image, text, type_)
|
||||
await message.edit("<b>Sending...</b>")
|
||||
if is_reply:
|
||||
await message.delete()
|
||||
return await event.reply(file=demotivator)
|
||||
return await event.edit(file=demotivator, text="")
|
||||
|
||||
|
||||
async def check_media(message):
|
||||
reply = await message.get_reply_message()
|
||||
is_reply = True
|
||||
if not reply:
|
||||
reply = message
|
||||
is_reply = False
|
||||
if not reply.file:
|
||||
return False, ...
|
||||
mime = reply.file.mime_type.split("/")[0].lower()
|
||||
if mime != "image":
|
||||
return False, ...
|
||||
return reply, is_reply
|
||||
|
||||
|
||||
async def textwrap(text, length=50, splitter="&&"):
|
||||
out = []
|
||||
lines = text.rsplit(splitter, 1)
|
||||
for text in lines:
|
||||
txt = []
|
||||
parts = text.split("&&")
|
||||
for part in parts:
|
||||
part = "\n".join(wrap(part, length))
|
||||
txt.append(part)
|
||||
text = "\n".join(txt)
|
||||
out.append(text)
|
||||
return out
|
||||
|
||||
|
||||
async def draw_main(
|
||||
bytes_image,
|
||||
type_,
|
||||
frame_width_1=5,
|
||||
frame_fill_1=(0, 0, 0),
|
||||
frame_width_2=3,
|
||||
frame_fill_2=(255, 255, 255),
|
||||
expand_proc=10,
|
||||
main_fill=(0, 0, 0),
|
||||
):
|
||||
main_ = Image.open(io.BytesIO(bytes_image))
|
||||
main = Image.new("RGB", main_.size, "black")
|
||||
main.paste(main_, (0, 0))
|
||||
if type_ == 1:
|
||||
main = main.resize((700, 550))
|
||||
main = ImageOps.expand(main, frame_width_1, frame_fill_1)
|
||||
main = ImageOps.expand(main, frame_width_2, frame_fill_2)
|
||||
w, h = main.size
|
||||
h_up = expand_proc * (h // 100)
|
||||
im = Image.new("RGB", (w + (h_up * 2), h + h_up), main_fill)
|
||||
im.paste(main, (h_up, h_up))
|
||||
return im
|
||||
|
||||
|
||||
async def _draw_text(
|
||||
text,
|
||||
font_bytes,
|
||||
font_size,
|
||||
font_add=30,
|
||||
main_fill=(0, 0, 0),
|
||||
text_fill=(255, 255, 255),
|
||||
text_align="center",
|
||||
):
|
||||
font = ImageFont.truetype(io.BytesIO(font_bytes), font_size)
|
||||
w_txt, h_txt = ImageDraw.Draw(Image.new("RGB", (1, 1))).multiline_textsize(
|
||||
text=text, font=font
|
||||
)
|
||||
txt = Image.new("RGB", (w_txt, h_txt + font_add), main_fill)
|
||||
ImageDraw.Draw(txt).text(
|
||||
(0, 0), text=text, font=font, fill=text_fill, align=text_align
|
||||
)
|
||||
return txt
|
||||
|
||||
|
||||
async def text_joiner(text_img_1, text_img_2, main_fill=(0, 0, 0)):
|
||||
w_txt_1, h_txt_1 = text_img_1.size
|
||||
w_txt_2, h_txt_2 = text_img_2.size
|
||||
w = max(w_txt_1, w_txt_2)
|
||||
h = h_txt_1 + h_txt_2
|
||||
text = Image.new("RGB", (w, h), main_fill)
|
||||
text.paste(text_img_1, ((w - w_txt_1) // 2, 0))
|
||||
text.paste(text_img_2, ((w - w_txt_2) // 2, h_txt_1))
|
||||
return text
|
||||
|
||||
|
||||
async def draw_text(text, font_bytes, font_size):
|
||||
text = await textwrap(text)
|
||||
if len(text) == 1:
|
||||
text = await _draw_text(text[0], font_bytes, font_size[0])
|
||||
else:
|
||||
text_img_1 = await _draw_text(text[0], font_bytes, font_size[0])
|
||||
text_img_2 = await _draw_text(text[-1], font_bytes, font_size[1])
|
||||
text = await text_joiner(text_img_1, text_img_2)
|
||||
return text
|
||||
|
||||
|
||||
async def text_finaller(text, main, expand_width_proc=25, main_fill=(0, 0, 0)):
|
||||
x = min(main.size)
|
||||
w_txt, h_txt = text.size
|
||||
w_proc = expand_width_proc * (w_txt // 100)
|
||||
h_proc = expand_width_proc * (h_txt // 100)
|
||||
back = Image.new("RGB", (w_txt + (w_proc * 2), h_txt + (h_proc * 2)), main_fill)
|
||||
back.paste(text, (w_proc, h_proc))
|
||||
back.thumbnail((x, x))
|
||||
return back
|
||||
|
||||
|
||||
async def joiner(text_img, main_img, format_save="JPEG"):
|
||||
w_im, h_im = main_img.size
|
||||
w_txt, h_txt = text_img.size
|
||||
text_img.thumbnail((min(w_im, h_im), min(w_im, h_im)))
|
||||
w_txt, h_txt = text_img.size
|
||||
main_img = main_img.crop((0, 0, w_im, h_im + h_txt))
|
||||
main_img.paste(text_img, ((w_im - w_txt) // 2, h_im))
|
||||
output = io.BytesIO()
|
||||
main_img.save(output, format_save)
|
||||
output.seek(0)
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
async def demotion(font_bytes, bytes_image, text, type):
|
||||
main = await draw_main(bytes_image, type)
|
||||
font_size = [20 * (min(main.size) // 100), 15 * (min(main.size) // 100)]
|
||||
text = await draw_text(text, font_bytes, font_size)
|
||||
text = await text_finaller(text, main)
|
||||
output = await joiner(text, main)
|
||||
return output
|
||||
|
||||
|
||||
async def demotionrand(font_bytes, bytes_image, text, type):
|
||||
main = await draw_main(bytes_image, type)
|
||||
font_size = [20 * (min(main.size) // 100), 15 * (min(main.size) // 100)]
|
||||
text = await draw_text(text, font_bytes, font_size)
|
||||
text = await text_finaller(text, main)
|
||||
output = await joiner(text, main)
|
||||
return output
|
||||
|
||||
|
||||
tttxxx = [
|
||||
"А че",
|
||||
"заставляет задуматься",
|
||||
"Жалко пацана",
|
||||
"ты че сука??",
|
||||
"ААХАХАХАХХАХА\n\nААХАХААХАХА",
|
||||
"ГИГАНТ МЫСЛИ\n\nотец русской демократии",
|
||||
"Он",
|
||||
"ЧТО БЛЯТЬ?",
|
||||
"охуенная тема",
|
||||
"ВОТ ОНИ\n\nтипичные комедиклабовские шутки",
|
||||
"НУ НЕ БЛЯДИНА?",
|
||||
"Узнали?",
|
||||
"Согласны?",
|
||||
"Вот это мужик",
|
||||
"ЕГО ИДЕИ\n\nбудут актуальны всегда",
|
||||
"\n\nПРИ СТАЛИНЕ ОН БЫ СИДЕЛ",
|
||||
"о вадим",
|
||||
"2 месяца на дваче\n\nи это, блядь, нихуя не смешно",
|
||||
"Что дальше?\n\nЧайник с функцией жопа?",
|
||||
"\n\nИ нахуя мне эта информация?",
|
||||
"Верхний текст",
|
||||
"нижний текст",
|
||||
"Показалось",
|
||||
"Суды при анкапе",
|
||||
"Хуйло с района\n\n\n\nтакая шелупонь с одной тычки ляжет",
|
||||
"Брух",
|
||||
"Расскажи им\n\nкак ты устал в офисе",
|
||||
"Окурок блять\n\nесть 2 рубля?",
|
||||
"Аниме ставшее легендой",
|
||||
"СМИРИСЬ\n\n\n\nты никогда не станешь настолько же крутым",
|
||||
"а ведь это идея",
|
||||
"\n\nЕсли не лайкнешь у тебя нет сердца",
|
||||
"Вместо тысячи слов",
|
||||
"ШАХ И МАТ!!!",
|
||||
"Самый большой член в мире\n\nУ этой девушки",
|
||||
"Немного\n\nперфекционизма",
|
||||
"кто",
|
||||
"\n\nэта сука уводит чужих мужей",
|
||||
"Кто он???",
|
||||
"\n\nВы тоже хотели насрать туда в детстве?",
|
||||
"\n\nВся суть современного общества\n\nв одном фото",
|
||||
"Он обязательно выживет!",
|
||||
"\n\nВы тоже хотите подрочить ему?",
|
||||
"\n\nИ вот этой хуйне поклоняются русские?",
|
||||
"Вот она суть\n\n\n\nчеловеческого общества в одной картинке",
|
||||
"Вы думали это рофл?\n\nНет это жопа",
|
||||
"\n\nПри сталине такой хуйни не было\n\nА у вас было?",
|
||||
"Он грыз провода",
|
||||
"Назло старухам\n\nна радость онанистам",
|
||||
"Где-то в Челябинске",
|
||||
"Агитация за Порошенко",
|
||||
"ИДЕАЛЬНО",
|
||||
"Грыз?",
|
||||
"Ну давай расскажи им\n\nкакая у тебя тяжелая работа",
|
||||
"\n\nЖелаю в каждом доме такого гостя",
|
||||
"Шкура на вырост",
|
||||
"НИКОГДА\n\nне сдавайся",
|
||||
"Оппа гангнам стайл\n\nуууу сэкси лейди оп оп",
|
||||
"Они сделали это\n\nсукины дети, они справились",
|
||||
"Эта сука\n\nхочет денег",
|
||||
"Это говно, а ты?",
|
||||
"\n\nВот она нынешняя молодежь",
|
||||
"Погладь кота\n\nпогладь кота сука",
|
||||
"Я обязательно выживу",
|
||||
"\n\nВот она, настоящая мужская дружба\n\nбез политики и лицимерия",
|
||||
"\n\nОБИДНО ЧТО Я ЖИВУ В СТРАНЕ\n\nгде гантели стоят в 20 раз "
|
||||
"дороже чем бутылка водки",
|
||||
"Царь, просто царь",
|
||||
"\n\nНахуй вы это в учебники вставили?\n\nИ ещё ебаную контрольную " "устроили",
|
||||
"\n\nЭТО НАСТОЯЩАЯ КРАСОТА\n\nа не ваши голые бляди",
|
||||
"\n\nТема раскрыта ПОЛНОСТЬЮ",
|
||||
"\n\nРОССИЯ, КОТОРУЮ МЫ ПОТЕРЯЛИ",
|
||||
"ЭТО - Я\n\nПОДУМАЙ МОЖЕТ ЭТО ТЫ",
|
||||
"почему\n\nчто почему",
|
||||
"КУПИТЬ БЫ ДЖЫП\n\nБЛЯТЬ ДА НАХУЙ НАДО",
|
||||
"\n\n\n\nмы не продаём бомбастер лицам старше 12 лет",
|
||||
"МРАЗЬ",
|
||||
"Правильная аэрография",
|
||||
"Вот она русская\n\nСМЕКАЛОЧКА",
|
||||
"Он взял рехстаг!\n\nА чего добился ты?",
|
||||
"На аватарку",
|
||||
"Фотошоп по-деревенски",
|
||||
"Инструкция в самолете",
|
||||
"Цирк дю Солей",
|
||||
"Вкус детства\n\nшколоте не понять",
|
||||
"Вот оно - СЧАСТЬЕ",
|
||||
"Он за тебя воевал\n\nа ты даже не знаешь его имени",
|
||||
"Зато не за компьютером",
|
||||
"\n\nНе трогай это на новый год",
|
||||
"Мой первый рисунок\n\nмочой на снегу",
|
||||
"\n\nМайские праздники на даче",
|
||||
"Ваш пиздюк?",
|
||||
"Тест драйв подгузников",
|
||||
"Не понимаю\n\nкак это вообще выросло?",
|
||||
"Супермен в СССР",
|
||||
"Единственный\n\nкто тебе рад",
|
||||
"Макдональдс отдыхает",
|
||||
"Ну че\n\n как дела на работе пацаны?",
|
||||
"Вся суть отношений",
|
||||
"Беларусы, спасибо!",
|
||||
"\n\nУ дверей узбекского военкомата",
|
||||
"Вместо 1000 слов",
|
||||
"Один вопрос\n\nнахуя?",
|
||||
"Ответ на санкции\n\nЕВРОПЫ",
|
||||
"ЦЫГАНСКИЕ ФОКУСЫ",
|
||||
"Блять!\n\nда он гений!",
|
||||
"\n\nУкраина ищет новые источники газа",
|
||||
"ВОТ ЭТО\n\nНАСТОЯЩИЕ КАЗАКИ а не ряженные",
|
||||
"Нового года не будет\n\nСанта принял Ислам",
|
||||
"\n\nОн был против наркотиков\n\nа ты и дальше убивай себя",
|
||||
"Всем похуй!\n\nВсем похуй!",
|
||||
"БРАТЬЯ СЛАВЯНЕ\n\nпомните друг о друге",
|
||||
"\n\nОН ПРИДУМАЛ ГОВНО\n\nа ты даже не знаешь его имени",
|
||||
"\n\nкраткий курс истории нацболов",
|
||||
"Эпоха ренессанса",
|
||||
]
|
||||
|
||||
|
||||
async def procces_img(message, way):
|
||||
cols = {
|
||||
"white": 1,
|
||||
"whit": 1,
|
||||
"whi": 1,
|
||||
"wh": 1,
|
||||
"w": 1,
|
||||
"black": 2,
|
||||
"blac": 2,
|
||||
"bla": 2,
|
||||
"bl": 2,
|
||||
"b": 2,
|
||||
}
|
||||
col = 1
|
||||
reply = await message.get_reply_message()
|
||||
txt = utils.get_args_raw(message)
|
||||
await message.edit("Waiting...")
|
||||
|
||||
if txt in cols:
|
||||
col = cols[txt]
|
||||
txt = None
|
||||
if not txt:
|
||||
txt = "я лошара."
|
||||
if not reply:
|
||||
await message.edit("Reply to photo/sticker")
|
||||
return
|
||||
if txt.split(" ")[0] in cols:
|
||||
col = cols[txt.split(" ")[0]]
|
||||
txt = " ".join(txt.split(" ")[1:])
|
||||
|
||||
bytes_font = requests.get(
|
||||
"https://github.com/Fl1yd/FTG-modules/blob/master/stuff/font3.ttf?raw=true"
|
||||
).content
|
||||
bytes_back = await reply.download_media(bytes)
|
||||
font = io.BytesIO(bytes_font)
|
||||
font = ImageFont.truetype(font, 72)
|
||||
img = Image.open(io.BytesIO(bytes_back))
|
||||
|
||||
W, H = img.size
|
||||
txt = txt.replace("\n", "𓃐")
|
||||
text = "\n".join(wrap(txt, 30))
|
||||
t = text
|
||||
t = t.replace("𓃐", "\n")
|
||||
|
||||
draw = ImageDraw.Draw(img)
|
||||
w, h = draw.multiline_textsize(t, font=font)
|
||||
imtext = Image.new("RGBA", (w + 20, h + 20), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(imtext)
|
||||
|
||||
if col == 2:
|
||||
draw.multiline_text((10, 10), t, (0, 0, 0), font=font, align="center")
|
||||
else:
|
||||
draw.multiline_text((10, 10), t, (255, 255, 255), font=font, align="center")
|
||||
imtext.thumbnail((W, H))
|
||||
w, h = imtext.size
|
||||
|
||||
if way == 1:
|
||||
img.paste(imtext, ((W - w) // 2, (H - h) // 1), imtext)
|
||||
if way == 2:
|
||||
img.paste(imtext, ((W - w) // 2, (H - h) // 15), imtext)
|
||||
if way == 3:
|
||||
img.paste(imtext, ((W - w) // 2, (H - h) // 2), imtext)
|
||||
|
||||
output = io.BytesIO()
|
||||
output.name = "клоун.png"
|
||||
img.save(output, "png")
|
||||
output.seek(0)
|
||||
await message.client.send_file(message.to_id, output, reply_to=reply)
|
||||
await message.delete()
|
||||
|
||||
|
||||
font_bytes = requests.get(
|
||||
"https://raw.githubusercontent.com/KeyZenD/l/master/times.ttf"
|
||||
).content
|
||||
#######################
|
||||
215
GeekTG/FTG-Modules/distort.py
Normal file
215
GeekTG/FTG-Modules/distort.py
Normal file
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
# requires: lottie cairosvg pillow wand
|
||||
|
||||
import io
|
||||
import os
|
||||
from random import choice, randint
|
||||
|
||||
from PIL import Image as IM
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
from wand.image import Image
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class DistortMod(loader.Module):
|
||||
"""Stickers or photo distort"""
|
||||
|
||||
strings = {
|
||||
"name": "Distort",
|
||||
"bad_input": "<b>Reply to image or stick!</b>",
|
||||
"processing": "<b>Distorting...</b>",
|
||||
"bad_input_tgs": "<b>Reply to animated sticker</b>",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
@loader.unrestricted
|
||||
async def tgscmd(self, message):
|
||||
"""Animated stickers distort"""
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await utils.answer(message, self.strings("bad_input_tgs", message))
|
||||
return
|
||||
if not reply.file:
|
||||
await utils.answer(message, self.strings("bad_input_tgs", message))
|
||||
return
|
||||
if not reply.file.name.endswith(".tgs"):
|
||||
await utils.answer(message, self.strings("bad_input_tgs", message))
|
||||
return
|
||||
message = await utils.answer(message, self.strings("processing", message))
|
||||
await reply.download_media("tgs.tgs")
|
||||
os.system("lottie_convert.py tgs.tgs json.json")
|
||||
with open("json.json", "r") as f:
|
||||
stick = f.read()
|
||||
f.close()
|
||||
|
||||
for i in range(1, randint(6, 10)):
|
||||
stick = choice(
|
||||
[
|
||||
stick.replace(f"[{i}]", f"[{(i + i) * 3}]"),
|
||||
stick.replace(f".{i}", f".{i}{i}"),
|
||||
]
|
||||
)
|
||||
|
||||
with open("json.json", "w") as f:
|
||||
f.write(stick)
|
||||
f.close()
|
||||
os.system("lottie_convert.py json.json tgs.tgs")
|
||||
with open("tgs.tgs", "rb") as f:
|
||||
file = io.BytesIO(f.read())
|
||||
file.name = "tgs.tgs"
|
||||
await utils.answer(message, file)
|
||||
os.remove("tgs.tgs")
|
||||
os.remove("json.json")
|
||||
|
||||
@loader.sudo
|
||||
async def distortcmd(self, message):
|
||||
""".distort <reply to photo>
|
||||
.distort im
|
||||
.distort 50
|
||||
.distort 50 im
|
||||
.distort im 50
|
||||
im => sends as photo
|
||||
50 => (from 0 to 100) percent of distortion, 0 is maximum distortion"""
|
||||
if message.is_reply:
|
||||
reply_message = await message.get_reply_message()
|
||||
data, mime = await check_media(reply_message)
|
||||
if isinstance(data, bool):
|
||||
await utils.answer(message, self.strings("bad_input", message))
|
||||
return
|
||||
else:
|
||||
await utils.answer(message, self.strings("bad_input", message))
|
||||
return
|
||||
rescale_rate = 70
|
||||
a = utils.get_args(message)
|
||||
force_file = False
|
||||
if a:
|
||||
if "im" in a:
|
||||
force_file = True
|
||||
a.remove("im")
|
||||
if len(a) > 0:
|
||||
if a[0].isdigit():
|
||||
rescale_rate = int(a[0])
|
||||
if rescale_rate <= 0:
|
||||
rescale_rate = 70
|
||||
|
||||
message = await utils.answer(message, self.strings("processing", message))
|
||||
file = await self.client.download_media(data, bytes)
|
||||
file, img = io.BytesIO(file), io.BytesIO()
|
||||
img.name = "img.png"
|
||||
IM.open(file).save(img, "PNG")
|
||||
media = await distort(io.BytesIO(img.getvalue()), rescale_rate)
|
||||
out, im = io.BytesIO(), IM.open(media)
|
||||
if force_file:
|
||||
mime = "png"
|
||||
out.name = f"out.{mime}"
|
||||
im.save(out, mime.upper())
|
||||
out.seek(0)
|
||||
|
||||
await utils.answer(message, out)
|
||||
|
||||
async def jpegdcmd(self, message):
|
||||
"""JPEG style distort"""
|
||||
if message.is_reply:
|
||||
reply_message = await message.get_reply_message()
|
||||
data = await check_mediaa(reply_message)
|
||||
if isinstance(data, bool):
|
||||
await utils.answer(message, self.strings("bad_input", message))
|
||||
return
|
||||
else:
|
||||
await utils.answer(message, self.strings("bad_input", message))
|
||||
return
|
||||
|
||||
message = await utils.answer(message, self.strings("processing", message))
|
||||
image = io.BytesIO()
|
||||
await self.client.download_media(data, image)
|
||||
image = IM.open(image)
|
||||
fried_io = io.BytesIO()
|
||||
fried_io.name = "image.jpeg"
|
||||
image = image.convert("RGB")
|
||||
image.save(fried_io, "JPEG", quality=0)
|
||||
fried_io.seek(0)
|
||||
await utils.answer(message, fried_io)
|
||||
|
||||
|
||||
async def distort(file, rescale_rate):
|
||||
img = Image(file=file)
|
||||
x, y = img.size[0], img.size[1]
|
||||
popx = int(rescale_rate * (x // 100))
|
||||
popy = int(rescale_rate * (y // 100))
|
||||
img.liquid_rescale(popx, popy, delta_x=1, rigidity=0)
|
||||
img.resize(x, y)
|
||||
out = io.BytesIO()
|
||||
out.name = "output.png"
|
||||
img.save(file=out)
|
||||
return io.BytesIO(out.getvalue())
|
||||
|
||||
|
||||
async def check_media(reply_message):
|
||||
mime = None
|
||||
if reply_message and reply_message.media:
|
||||
if reply_message.photo:
|
||||
data = reply_message.photo
|
||||
mime = "image/jpeg"
|
||||
elif reply_message.document:
|
||||
if (
|
||||
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
|
||||
in reply_message.media.document.attributes
|
||||
):
|
||||
return False, mime
|
||||
if (
|
||||
reply_message.gif
|
||||
or reply_message.video
|
||||
or reply_message.audio
|
||||
or reply_message.voice
|
||||
):
|
||||
return False, mime
|
||||
data = reply_message.media.document
|
||||
mime = reply_message.media.document.mime_type
|
||||
if "image/" not in mime:
|
||||
return False, mime
|
||||
else:
|
||||
return False, mime
|
||||
else:
|
||||
return False, mime
|
||||
|
||||
if not data or data is None:
|
||||
return False, mime
|
||||
else:
|
||||
mime = mime.split("/")[1]
|
||||
return data, mime
|
||||
|
||||
|
||||
async def check_mediaa(reply_message):
|
||||
if reply_message and reply_message.media:
|
||||
if reply_message.photo:
|
||||
data = reply_message.photo
|
||||
elif reply_message.document:
|
||||
if (
|
||||
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
|
||||
in reply_message.media.document.attributes
|
||||
):
|
||||
return False
|
||||
if (
|
||||
reply_message.gif
|
||||
or reply_message.video
|
||||
or reply_message.audio
|
||||
or reply_message.voice
|
||||
):
|
||||
return False
|
||||
data = reply_message.media.document
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
if not data or data is None:
|
||||
return False
|
||||
else:
|
||||
return data
|
||||
184
GeekTG/FTG-Modules/downloader.py
Normal file
184
GeekTG/FTG-Modules/downloader.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules, @ftgmodulesbyfl1yd
|
||||
|
||||
import io
|
||||
import os
|
||||
from asyncio import sleep
|
||||
|
||||
from requests import get
|
||||
from telethon import events, functions
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
from telethon.tl.types import MessageEntityTextUrl, MessageEntityUrl
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class DownloaderMod(loader.Module):
|
||||
"""Downloader module"""
|
||||
|
||||
strings = {"name": "Downloader"}
|
||||
|
||||
async def dlrcmd(self, message):
|
||||
""".dlr <path/file_name> - download file to server"""
|
||||
name = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if reply:
|
||||
await message.edit("Downloading...")
|
||||
if reply.text:
|
||||
text = reply.text
|
||||
fname = f"{name or message.id + reply.id}.txt"
|
||||
with open(fname, "w") as file:
|
||||
file.write(text)
|
||||
else:
|
||||
ext = reply.file.ext
|
||||
fname = f"{name or message.id + reply.id}{ext}"
|
||||
await message.client.download_media(reply, fname)
|
||||
await message.edit(
|
||||
f"FIle saved as: <code>{fname}</code>.\n\nYou "
|
||||
f"can send it with command: "
|
||||
f"<code>.ulf {fname}</code>."
|
||||
)
|
||||
else:
|
||||
return await message.edit("There is no reply")
|
||||
|
||||
async def ulfcmd(self, message):
|
||||
""".ulf <file_name/path> send file from server
|
||||
<d> - Delete file after sending"""
|
||||
name = utils.get_args_raw(message)
|
||||
d = False
|
||||
if "d " in name:
|
||||
d = True
|
||||
if not name:
|
||||
return await message.edit("No args")
|
||||
try:
|
||||
name = name.replace("d ", "")
|
||||
await message.edit(f"Sending <code>{name}</code>...")
|
||||
if d:
|
||||
await message.client.send_file(message.to_id, f"{name}")
|
||||
await message.edit(
|
||||
f"Sending <code>{name}</code>... Success!\Deleting "
|
||||
f"<code>{name}</code>..."
|
||||
)
|
||||
os.remove(name)
|
||||
await message.edit(
|
||||
f"Sending <code>{name}</code>... Deleting!\nУдаляем "
|
||||
f"<code>{name}</code>... Success!"
|
||||
)
|
||||
await sleep(0.5)
|
||||
else:
|
||||
await message.client.send_file(message.to_id, name)
|
||||
except:
|
||||
return await message.edit("File does not exist")
|
||||
await message.delete()
|
||||
|
||||
async def dltiktokcmd(self, message):
|
||||
"""TikTok video downloader"""
|
||||
chat = "@ttsavebot"
|
||||
reply = await message.get_reply_message()
|
||||
async with message.client.conversation(chat) as conv:
|
||||
text = utils.get_args_raw(message)
|
||||
if reply:
|
||||
text = await message.get_reply_message()
|
||||
await message.edit("<b>Downloading...</b>")
|
||||
try:
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1087584961)
|
||||
)
|
||||
response2 = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1087584961)
|
||||
)
|
||||
response3 = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1087584961)
|
||||
)
|
||||
mm = await message.client.send_message(chat, text)
|
||||
response = await response
|
||||
response2 = await response2
|
||||
response3 = await response3
|
||||
await mm.delete()
|
||||
except YouBlockedUserError:
|
||||
await message.edit("<code>Разблокируй @ttsavebot</code>")
|
||||
return
|
||||
await message.client.send_file(
|
||||
message.to_id, response3.media, reply_to=reply
|
||||
)
|
||||
await message.delete()
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer="ttsavebot", max_id=0, just_clear=False, revoke=True
|
||||
)
|
||||
)
|
||||
|
||||
async def dlfilecmd(self, message):
|
||||
"""File downloader (small files)"""
|
||||
await downloading(message)
|
||||
|
||||
async def dlbigfilecmd(self, message):
|
||||
"""File downloader (big files)"""
|
||||
await downloading(message, True)
|
||||
|
||||
|
||||
async def downloading(message, big=False):
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not args:
|
||||
if not reply:
|
||||
await message.edit("<b>There is no link!</b>")
|
||||
return
|
||||
message = reply
|
||||
else:
|
||||
message = message
|
||||
|
||||
if not message.entities:
|
||||
await message.edit("<b>There is no link!</b>")
|
||||
return
|
||||
|
||||
urls = []
|
||||
for ent in message.entities:
|
||||
if type(ent) in [MessageEntityUrl, MessageEntityTextUrl]:
|
||||
if type(ent) == MessageEntityUrl:
|
||||
offset = ent.offset
|
||||
length = ent.length
|
||||
url = message.raw_text[offset : offset + length]
|
||||
else:
|
||||
url = ent.url
|
||||
if not url.startswith("http"):
|
||||
url = "http://" + url
|
||||
urls.append(url)
|
||||
|
||||
if not urls:
|
||||
await message.edit("<b>There is no link!</b>")
|
||||
return
|
||||
for url in urls:
|
||||
try:
|
||||
await message.edit("<b>Downloading...</b>\n" + url)
|
||||
fname = url.split("/")[-1]
|
||||
text = get(url, stream=big)
|
||||
if big:
|
||||
f = open(fname, "wb")
|
||||
for chunk in text.iter_content(1024):
|
||||
f.write(chunk)
|
||||
f.close()
|
||||
await message.edit("<b>Sending...</b>\n" + url)
|
||||
await message.client.send_file(
|
||||
message.to_id, open(fname, "rb"), reply_to=reply
|
||||
)
|
||||
os.remove(fname)
|
||||
else:
|
||||
file = io.BytesIO(text.content)
|
||||
file.name = fname
|
||||
file.seek(0)
|
||||
await message.edit("<b>Sending...</b>\n" + url)
|
||||
await message.client.send_file(message.to_id, file, reply_to=reply)
|
||||
|
||||
except Exception as e:
|
||||
await message.reply(
|
||||
"<b>Error while downloading!</b>\n"
|
||||
+ url
|
||||
+ "\n<code>"
|
||||
+ str(e)
|
||||
+ "</code>"
|
||||
)
|
||||
|
||||
await message.delete()
|
||||
201
GeekTG/FTG-Modules/fake_actions.py
Normal file
201
GeekTG/FTG-Modules/fake_actions.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd, @GovnoCodules
|
||||
|
||||
from asyncio import sleep
|
||||
from random import randint
|
||||
|
||||
from telethon import functions
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FakeMod(loader.Module):
|
||||
"""Imitates your actions"""
|
||||
|
||||
strings = {"name": "Fake Actions"}
|
||||
|
||||
async def typecmd(self, message):
|
||||
"""Imitates typing"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "typing"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "typing"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def voicecmd(self, message):
|
||||
"""Imitates sending voices"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "voice"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "voice"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def gamecmd(self, message):
|
||||
"""Imitates your game activity"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "game"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "game"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def videocmd(self, message):
|
||||
"""Imitates sending video"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "video"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "video"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def photocmd(self, message):
|
||||
"""Imitates sending photo"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "photo"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "photo"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def documentcmd(self, message):
|
||||
"""Imitates sending document"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "document"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "document"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def locationcmd(self, message):
|
||||
"""Imitates sending location"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "location"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "location"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def recordvideocmd(self, message):
|
||||
"""Imitates recording video"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-video"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-video"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def recordvoicecmd(self, message):
|
||||
"""Imitates recording voice"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-audio"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-audio"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def recordroundcmd(self, message):
|
||||
"""Imitates recording round video"""
|
||||
activity_time = utils.get_args(message)
|
||||
await message.delete()
|
||||
if activity_time:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-round"):
|
||||
await sleep(int(activity_time[0]))
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
async with message.client.action(message.chat_id, "record-round"):
|
||||
await sleep(randint(30, 60))
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
async def scrncmd(self, message):
|
||||
"""Screenshot notification (Only PM)"""
|
||||
a = 1
|
||||
r = utils.get_args(message)
|
||||
if r and r[0].isdigit():
|
||||
a = int(r[0])
|
||||
for _ in range(a):
|
||||
await message.client(
|
||||
functions.messages.SendScreenshotNotificationRequest(
|
||||
peer=message.to_id, reply_to_msg_id=message.id
|
||||
)
|
||||
)
|
||||
await message.delete()
|
||||
153
GeekTG/FTG-Modules/file_uploader.py
Normal file
153
GeekTG/FTG-Modules/file_uploader.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
import io
|
||||
import logging
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from requests import post
|
||||
from telethon import events
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FileUploaderMod(loader.Module):
|
||||
"""Uploader"""
|
||||
|
||||
strings = {"name": "File Uploader"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
@loader.sudo
|
||||
async def x0cmd(self, message):
|
||||
"""Upload to x0"""
|
||||
await message.edit("<b>Uploading...</b>")
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.edit("<b>Reply to message!</b>")
|
||||
return
|
||||
media = reply.media
|
||||
if not media:
|
||||
file = io.BytesIO(bytes(reply.raw_text, "utf-8"))
|
||||
file.name = "txt.txt"
|
||||
else:
|
||||
file = io.BytesIO(await self.client.download_file(media))
|
||||
file.name = reply.file.name or reply.file.id + reply.file.ext
|
||||
try:
|
||||
x0at = post("https://x0.at", files={"file": file})
|
||||
except ConnectionError:
|
||||
await message.edit("<b>Error</b>")
|
||||
return
|
||||
url = x0at.text
|
||||
output = f'<a href="{url}">URL: </a><code>{url}</code>'
|
||||
await message.edit(output)
|
||||
|
||||
async def telegraphcmd(self, message):
|
||||
""".ph <reply photo or video>"""
|
||||
if message.is_reply:
|
||||
reply_message = await message.get_reply_message()
|
||||
data = await check_media(reply_message)
|
||||
if isinstance(data, bool):
|
||||
await message.edit("<b>Reply to photo or video/gif</b>")
|
||||
return
|
||||
else:
|
||||
await message.edit("<b>Reply to photo or video/gif</b>")
|
||||
return
|
||||
|
||||
file = await message.client.download_media(data, bytes)
|
||||
path = requests.post(
|
||||
"https://te.legra.ph/upload", files={"file": ("file", file, None)}
|
||||
).json()
|
||||
try:
|
||||
link = "https://te.legra.ph" + path[0]["src"]
|
||||
except KeyError:
|
||||
link = path["error"]
|
||||
await message.edit("<b>" + link + "</b>")
|
||||
|
||||
async def imgurcmd(self, message):
|
||||
"""Upload to imgur"""
|
||||
chat = "@ImgUploadBot"
|
||||
reply = await message.get_reply_message()
|
||||
async with message.client.conversation(chat) as conv:
|
||||
if not reply:
|
||||
await message.edit("<b>Reply to photo</b>")
|
||||
return
|
||||
else:
|
||||
pic = await check_mediaa(message, reply)
|
||||
if not pic:
|
||||
await utils.answer(message, "<b>Reply to photo</b>")
|
||||
return
|
||||
await message.edit("<b>Uploading...</b>")
|
||||
try:
|
||||
what = lol(pic)
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=985223903)
|
||||
)
|
||||
await message.client.send_file(chat, what)
|
||||
response = await response
|
||||
except YouBlockedUserError:
|
||||
await message.edit("<code>Разблокируй @imgurbot_bot</code>")
|
||||
return
|
||||
await message.edit("<b>Imgur link - </b>" + response.text)
|
||||
|
||||
|
||||
async def check_media(reply_message):
|
||||
if reply_message and reply_message.media:
|
||||
if reply_message.photo:
|
||||
data = reply_message.photo
|
||||
elif reply_message.document:
|
||||
if (
|
||||
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
|
||||
in reply_message.media.document.attributes
|
||||
):
|
||||
return False
|
||||
if reply_message.audio or reply_message.voice:
|
||||
return False
|
||||
data = reply_message.media.document
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
if not data or data is None:
|
||||
return False
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
def lol(reply):
|
||||
scrrrra = Image.open(BytesIO(reply))
|
||||
out = io.BytesIO()
|
||||
out.name = "outsider.png"
|
||||
scrrrra.save(out)
|
||||
return out.getvalue()
|
||||
|
||||
|
||||
async def check_mediaa(message, reply):
|
||||
if reply and reply.media:
|
||||
if reply.photo:
|
||||
data = reply.photo
|
||||
elif reply.document:
|
||||
if reply.gif or reply.video or reply.audio or reply.voice:
|
||||
return None
|
||||
data = reply.media.document
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
if not data or data is None:
|
||||
return None
|
||||
data = await message.client.download_file(data, bytes)
|
||||
try:
|
||||
Image.open(io.BytesIO(data))
|
||||
return data
|
||||
except:
|
||||
return None
|
||||
142
GeekTG/FTG-Modules/filter.py
Normal file
142
GeekTG/FTG-Modules/filter.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FiltersMod(loader.Module):
|
||||
"""Filters module"""
|
||||
|
||||
strings = {"name": "Filters"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
|
||||
async def filtercmd(self, message):
|
||||
"""Adds a filter into the list."""
|
||||
filters = self.db.get("Filters", "filters", {})
|
||||
key = utils.get_args_raw(message).lower()
|
||||
reply = await message.get_reply_message()
|
||||
chatid = str(message.chat_id)
|
||||
|
||||
if not key and not reply:
|
||||
return await message.edit("<b>No args or reply.</b>")
|
||||
|
||||
if chatid not in filters:
|
||||
filters.setdefault(chatid, {})
|
||||
|
||||
if key in filters[chatid]:
|
||||
return await message.edit("<b>Such a filter already exists.</b>")
|
||||
|
||||
if reply:
|
||||
if key:
|
||||
msgid = await self.db.store_asset(reply)
|
||||
else:
|
||||
return await message.edit(
|
||||
"<b>You need arguments to save the filter!</b>"
|
||||
)
|
||||
else:
|
||||
try:
|
||||
msgid = (
|
||||
await message.client.send_message(
|
||||
f"friendly-{(await message.client.get_me()).id}-assets",
|
||||
key.split("/")[1],
|
||||
)
|
||||
).id
|
||||
key = key.split("/")[0]
|
||||
except IndexError:
|
||||
return await message.edit(
|
||||
"<b>Need a second argument (through / ) or a reply.</b>"
|
||||
)
|
||||
|
||||
filters[chatid].setdefault(key, msgid)
|
||||
self.db.set("Filters", "filters", filters)
|
||||
await message.edit(f'<b>Filter "{key}" saved!</b>')
|
||||
|
||||
async def stopcmd(self, message):
|
||||
"""Removes a filter from the list."""
|
||||
filters = self.db.get("Filters", "filters", {})
|
||||
args = utils.get_args_raw(message)
|
||||
chatid = str(message.chat_id)
|
||||
|
||||
if chatid not in filters:
|
||||
return await message.edit("<b>There are no filters in this chat.</b>")
|
||||
|
||||
if not args:
|
||||
return await message.edit("<b>No args.</b>")
|
||||
|
||||
if args:
|
||||
try:
|
||||
filters[chatid].pop(args)
|
||||
self.db.set("Filters", "filters", filters)
|
||||
await message.edit(f'<b>Filter "{args}" removed from chat list!</b>')
|
||||
except KeyError:
|
||||
return await message.edit(f'<b>No "{args}" filter.</b>')
|
||||
else:
|
||||
return await message.edit("<b>No args.</b>")
|
||||
|
||||
async def stopallcmd(self, message):
|
||||
"""Clears out the filter list."""
|
||||
filters = self.db.get("Filters", "filters", {})
|
||||
chatid = str(message.chat_id)
|
||||
|
||||
if chatid not in filters:
|
||||
return await message.edit("<b>There are no filters in this chat.</b>")
|
||||
|
||||
filters.pop(chatid)
|
||||
self.db.set("Filters", "filters", filters)
|
||||
await message.edit("<b>All filters have been removed from the chat list!</b>")
|
||||
|
||||
async def filterscmd(self, message):
|
||||
"""Shows saved filters."""
|
||||
filters = self.db.get("Filters", "filters", {})
|
||||
chatid = str(message.chat_id)
|
||||
|
||||
if chatid not in filters:
|
||||
return await message.edit("<b>There are no filters in this chat.</b>")
|
||||
|
||||
msg = ""
|
||||
for _ in filters[chatid]:
|
||||
msg += f"<b>• {_}</b>\n"
|
||||
await message.edit(
|
||||
f"<b>List of filters in this chat: {len(filters[chatid])}\n\n{msg}</b>"
|
||||
)
|
||||
|
||||
async def watcher(self, message):
|
||||
try:
|
||||
filters = self.db.get("Filters", "filters", {})
|
||||
chatid = str(message.chat_id)
|
||||
m = message.text.lower()
|
||||
if chatid not in filters:
|
||||
return
|
||||
|
||||
for _ in filters[chatid]:
|
||||
msg = await self.db.fetch_asset(filters[chatid][_])
|
||||
def_pref = self.db.get("friendly-telegram.main", "command_prefix")
|
||||
pref = "." if not def_pref else def_pref[0]
|
||||
|
||||
if len(_.split()) == 1:
|
||||
if _ in m.split():
|
||||
await self.exec_comm(msg, message, pref)
|
||||
else:
|
||||
if _ in m:
|
||||
await self.exec_comm(msg, message, pref)
|
||||
except:
|
||||
pass
|
||||
|
||||
async def exec_comm(self, msg, message, pref):
|
||||
try:
|
||||
if msg.text[0] == pref:
|
||||
smsg = msg.text.split()
|
||||
return await self.allmodules.commands[smsg[0][1:]](
|
||||
await message.reply(
|
||||
smsg[0] + " ".join(_ for _ in smsg if len(smsg) > 1)
|
||||
)
|
||||
)
|
||||
else:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
await message.reply(msg)
|
||||
50
GeekTG/FTG-Modules/full.txt
Normal file
50
GeekTG/FTG-Modules/full.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
admin_tools
|
||||
audio_editor
|
||||
autoprofile
|
||||
avatar
|
||||
banwords
|
||||
calculator
|
||||
callcontrol
|
||||
chat
|
||||
chatvoicemod
|
||||
conthelper
|
||||
demot
|
||||
distort
|
||||
downloader
|
||||
fake_actions
|
||||
file_uploader
|
||||
filter
|
||||
image_editor
|
||||
image_tools
|
||||
imgtfy
|
||||
information
|
||||
lyrics
|
||||
morze
|
||||
music
|
||||
noterminal
|
||||
notes
|
||||
notexec
|
||||
pmlog
|
||||
purge
|
||||
qr_code
|
||||
quotes
|
||||
range
|
||||
recent_actions
|
||||
rpmod
|
||||
screenshot
|
||||
searcher
|
||||
squotes
|
||||
stickers
|
||||
tags
|
||||
terminal
|
||||
text_generator
|
||||
translate
|
||||
tts
|
||||
url
|
||||
video_editor
|
||||
vizjener
|
||||
voice_recognition
|
||||
warn
|
||||
weather
|
||||
welcome
|
||||
ytdl
|
||||
245
GeekTG/FTG-Modules/image_editor.py
Normal file
245
GeekTG/FTG-Modules/image_editor.py
Normal file
@@ -0,0 +1,245 @@
|
||||
"""
|
||||
.------.------.------.------.------.------.------.------.------.------.
|
||||
|D.--. |4.--. |N.--. |1.--. |3.--. |L.--. |3.--. |K.--. |0.--. |0.--. |
|
||||
| :/\: | :/\: | :(): | :/\: | :(): | :/\: | :(): | :/\: | :/\: | :/\: |
|
||||
| (__) | :\/: | ()() | (__) | ()() | (__) | ()() | :\/: | :\/: | :\/: |
|
||||
| '--'D| '--'4| '--'N| '--'1| '--'3| '--'L| '--'3| '--'K| '--'0| '--'0|
|
||||
`------`------`------`------`------`------`------`------`------`------'
|
||||
|
||||
Copyright 2022 t.me/D4n13l3k00
|
||||
Licensed under the Creative Commons CC BY-NC-ND 4.0
|
||||
|
||||
Full license text can be found at:
|
||||
https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
||||
|
||||
Human-friendly one:
|
||||
https://creativecommons.org/licenses/by-nc-nd/4.0
|
||||
"""
|
||||
# meta developer: @D4n13l3k00
|
||||
|
||||
|
||||
# requires: Pillow aiohttp fake-useragent
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import re
|
||||
from datetime import date
|
||||
|
||||
import aiohttp
|
||||
from fake_useragent import UserAgent
|
||||
from PIL import Image, ImageEnhance, ImageOps
|
||||
from telethon import types
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ImageEditorMod(loader.Module):
|
||||
"ImageEditor - Simple tool for working with images"
|
||||
strings = {
|
||||
"name": "ImageEditor",
|
||||
"downloading": "<b>[{}]</b> Downloading...",
|
||||
"working": "<b>[{}]</b> Working...",
|
||||
"exporting": "<b>[{}]</b> Exporting...",
|
||||
"set_value": "<b>[{}]</b> Specify the level...",
|
||||
"set_size": "<b>[{}]</b> Specify the size...",
|
||||
"reply": "<b>[{}]</b> reply to image...",
|
||||
"set_time": "<b>[{}]</b> Specify the time in the format start(ms):end(ms)",
|
||||
}
|
||||
|
||||
@loader.owner
|
||||
async def resizeicmd(self, m: types.Message):
|
||||
".resizei <w> <h> - Resize image"
|
||||
_pref = "Resize"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d+)\s+(\d+)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_size", m).format(_pref))
|
||||
w, h = [int(i) for i in r.match(args).groups()]
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = im.image.resize((w, h))
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def rmbgicmd(self, m: types.Message):
|
||||
".rmbgi - Remove background via AI [Powered by Indian's AI]"
|
||||
_pref = "RemoveBg"
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
b = io.BytesIO()
|
||||
b.name = "i.png"
|
||||
im.image.save(b, "PNG")
|
||||
b.seek(0)
|
||||
out = None
|
||||
async with aiohttp.ClientSession(
|
||||
headers={"User-Agent": UserAgent().chrome}
|
||||
) as s:
|
||||
form = aiohttp.FormData()
|
||||
form.add_field("file", b)
|
||||
form.add_field("filenameOverride", "true")
|
||||
form.add_field(
|
||||
"path",
|
||||
f"__editor/{date.year}-{date.month}-{date.day}/{hashlib.md5(b.read()).hexdigest()}",
|
||||
)
|
||||
b.seek(0)
|
||||
async with s.post(
|
||||
"https://api.erase.bg/service/panel/assets/v1.0/upload/direct",
|
||||
data=form,
|
||||
) as r:
|
||||
_url = (await r.json())["url"]
|
||||
async with s.get(
|
||||
_url.replace("dummy-cloudname/original", "dummy-cloudname/erase.bg()")
|
||||
) as r:
|
||||
i = io.BytesIO(await r.read())
|
||||
i.name = "ImageEditor.jpeg"
|
||||
out = Image.open(i)
|
||||
await go_out(self, m, im, out, _pref, True)
|
||||
|
||||
@loader.owner
|
||||
async def inverticmd(self, m: types.Message):
|
||||
".inverti - Invert colors"
|
||||
_pref = "Invert"
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = ImageOps.invert(im.image)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def bwicmd(self, m: types.Message):
|
||||
".bwi - BlackWhite"
|
||||
_pref = "BlackWhite"
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = im.image.convert("L")
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def convicmd(self, m: types.Message):
|
||||
".convi - Sticker to image | Image to sticker"
|
||||
_pref = "Converter"
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
im.is_webp = not im.is_webp
|
||||
await go_out(self, m, im, im.image, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def rotateicmd(self, m: types.Message):
|
||||
".rotatei <degrees> - Rotate image"
|
||||
_pref = "Rotate"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d+)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_value", m).format(_pref))
|
||||
degrees = int(r.match(args).groups()[0])
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = im.image.rotate(degrees, expand=True)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def contrasticmd(self, m: types.Message):
|
||||
".contrasti <float> - Change contrast"
|
||||
_pref = "Contrast"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d*\.?\d*)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_value", m).format(_pref))
|
||||
level = float(r.match(args).groups()[0])
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = ImageEnhance.Contrast(im.image).enhance(level)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def sharpnessicmd(self, m: types.Message):
|
||||
".sharpnessi <float> - Change sharpness"
|
||||
_pref = "Sharpness"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d*\.?\d*)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_value", m).format(_pref))
|
||||
level = float(r.match(args).groups()[0])
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = ImageEnhance.Sharpness(im.image).enhance(level)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def brighticmd(self, m: types.Message):
|
||||
".brighti <float> - Change bright"
|
||||
_pref = "Color"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d*\.?\d*)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_value", m).format(_pref))
|
||||
level = float(r.match(args).groups()[0])
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = ImageEnhance.Brightness(im.image).enhance(level)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
@loader.owner
|
||||
async def coloricmd(self, m: types.Message):
|
||||
".colori <float> - Change color factor"
|
||||
_pref = "Color"
|
||||
args = utils.get_args_raw(m)
|
||||
r = re.compile(r"^(\d*\.?\d*)$")
|
||||
if not args or not r.match(args):
|
||||
return await utils.answer(m, self.strings("set_value", m).format(_pref))
|
||||
level = float(r.match(args).groups()[0])
|
||||
im = await get_image(self, m, _pref)
|
||||
if not im:
|
||||
return
|
||||
out = ImageEnhance.Color(im.image).enhance(level)
|
||||
await go_out(self, m, im, out, _pref)
|
||||
|
||||
|
||||
class ImageEditorClass:
|
||||
image: Image.Image = None
|
||||
message = None
|
||||
is_webp: bool = None
|
||||
pref: str = None
|
||||
reply = None
|
||||
|
||||
|
||||
async def get_image(self, m, pref: str) -> ImageEditorClass:
|
||||
r = await m.get_reply_message()
|
||||
if r and r.file and r.file.mime_type.split("/")[0] in ["image"]:
|
||||
im = ImageEditorClass()
|
||||
im.pref = pref
|
||||
im.reply = r
|
||||
im.is_webp = r.file.ext == ".webp"
|
||||
m = await utils.answer(m, self.strings("downloading", m).format(pref))
|
||||
im.image = Image.open(io.BytesIO(await r.download_media(bytes)))
|
||||
im.message = await utils.answer(m, self.strings("working", m).format(pref))
|
||||
return im
|
||||
await utils.answer(m, self.strings("reply", m).format(pref))
|
||||
|
||||
|
||||
async def go_out(
|
||||
self, m, im: ImageEditorClass, out: Image.Image, pref, force_document=False
|
||||
):
|
||||
m = await utils.answer(m, self.strings("exporting", m).format(pref))
|
||||
iba = io.BytesIO()
|
||||
if im.is_webp:
|
||||
out.thumbnail((512, 512))
|
||||
out.save(iba, format="WEBP" if im.is_webp else "PNG")
|
||||
iba.name = "ImageEditor." + ("webp" if im.is_webp else "png")
|
||||
iba.seek(0)
|
||||
await utils.answer(
|
||||
m,
|
||||
iba,
|
||||
reply_to=im.reply.id,
|
||||
supports_streaming=True,
|
||||
force_document=force_document if not im.is_webp else False,
|
||||
)
|
||||
487
GeekTG/FTG-Modules/image_tools.py
Normal file
487
GeekTG/FTG-Modules/image_tools.py
Normal file
@@ -0,0 +1,487 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
import io
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
from io import BytesIO as ist
|
||||
from random import randint, uniform
|
||||
|
||||
from PIL import Image, ImageDraw, ImageEnhance
|
||||
from PIL import ImageOps
|
||||
from PIL import ImageOps as IO
|
||||
from telethon import events
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
from telethon.tl.types import DocumentAttributeFilename as DAF
|
||||
|
||||
from .. import loader
|
||||
from .. import utils
|
||||
from .. import utils as U
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_C = "png"
|
||||
_B = "name"
|
||||
_A = "image"
|
||||
_R = "отражает"
|
||||
_P = "часть."
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ImageToolsMod(loader.Module):
|
||||
"""Image tools module"""
|
||||
|
||||
strings = {"name": "Image Tools"}
|
||||
|
||||
async def llcmd(A, message):
|
||||
"""Mirror the image"""
|
||||
await KZD(message, 1)
|
||||
|
||||
async def rrcmd(A, message):
|
||||
"""Mirror the image"""
|
||||
await KZD(message, 2)
|
||||
|
||||
async def uucmd(A, message):
|
||||
"""Mirror the image"""
|
||||
await KZD(message, 3)
|
||||
|
||||
async def ddcmd(A, message):
|
||||
"""Mirror the image"""
|
||||
await KZD(message, 4)
|
||||
|
||||
@loader.unrestricted
|
||||
async def dotifycmd(self, message):
|
||||
"""Image to RGB dots"""
|
||||
mode = False
|
||||
reply, pix = await parse(message)
|
||||
if reply:
|
||||
await dotify(message, reply, pix, mode)
|
||||
|
||||
async def dotificmd(self, message):
|
||||
"""Image to BW dots"""
|
||||
mode = True
|
||||
reply, pix = await parse(message)
|
||||
if reply:
|
||||
await dotify(message, reply, pix, mode)
|
||||
|
||||
@loader.sudo
|
||||
async def soapcmd(self, message):
|
||||
""".soap <reply to photo>"""
|
||||
soap = 3
|
||||
a = utils.get_args(message)
|
||||
if a and a[0].isdigit():
|
||||
soap = int(a[0])
|
||||
if soap <= 0:
|
||||
soap = 3
|
||||
|
||||
if message.is_reply:
|
||||
reply_message = await message.get_reply_message()
|
||||
data = await check_media(reply_message)
|
||||
if isinstance(data, bool):
|
||||
await utils.answer(message, "<code>Reply to pic or stick!</code>")
|
||||
return
|
||||
else:
|
||||
await utils.answer(message, "<code>Reply to pic or stick!</code>")
|
||||
return
|
||||
|
||||
await message.edit("Soaping...")
|
||||
file = await message.client.download_media(data, bytes)
|
||||
media = await Soaping(file, soap)
|
||||
await message.delete()
|
||||
|
||||
await message.client.send_file(message.to_id, media)
|
||||
|
||||
async def pic2packcmd(self, message):
|
||||
"""Create sticker pack with your photo"""
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.edit("<b>Reply to photo❗</b>")
|
||||
return
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
await message.edit("<b>Packname</b>❓")
|
||||
return
|
||||
chat = "@Stickers"
|
||||
name = "".join(
|
||||
random.choice(list(string.ascii_lowercase + string.ascii_uppercase))
|
||||
for _ in range(16)
|
||||
)
|
||||
image = io.BytesIO()
|
||||
await message.client.download_file(reply, image)
|
||||
image = Image.open(image)
|
||||
w, h = image.size
|
||||
www = max(w, h)
|
||||
await message.edit("🔪<b>Cropping...</b>")
|
||||
img = Image.new("RGBA", (www, www), (0, 0, 0, 0))
|
||||
img.paste(image, ((www - w) // 2, 0))
|
||||
face = img.resize((100, 100))
|
||||
fface = io.BytesIO()
|
||||
fface.name = name + ".png"
|
||||
images = await cropping(img)
|
||||
face.save(fface)
|
||||
fface.seek(0)
|
||||
await message.edit("<b>📤Uploading...</b>")
|
||||
async with message.client.conversation(chat) as conv:
|
||||
emoji = "▫️"
|
||||
try:
|
||||
x = await message.client.send_message(chat, "/cancel")
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
x = await message.client.send_message(chat, "/newpack")
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
x = await message.client.send_message(chat, args)
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
|
||||
for im in images:
|
||||
blank = io.BytesIO(im)
|
||||
blank.name = name + ".png"
|
||||
blank.seek(0)
|
||||
x = await message.client.send_file(chat, blank, force_document=True)
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
x = await message.client.send_message(chat, emoji)
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
|
||||
x = await message.client.send_message(chat, "/publish")
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
x = await message.client.send_file(chat, fface, force_document=True)
|
||||
await (
|
||||
await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
).delete()
|
||||
await x.delete()
|
||||
x = await message.client.send_message(chat, name)
|
||||
ending = await conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=chat)
|
||||
)
|
||||
await x.delete()
|
||||
await ending.delete()
|
||||
for part in ending.raw_text.split():
|
||||
if part.startswith("https://t.me/"):
|
||||
break
|
||||
await message.edit("✅<b>Uploaded successful!</b>\n" + part)
|
||||
|
||||
except YouBlockedUserError:
|
||||
await message.edit("<b>@Stickers BLOCKED⛔</b>")
|
||||
return
|
||||
|
||||
async def deepcmd(self, message):
|
||||
"""Deep the image"""
|
||||
try:
|
||||
frycount = int(utils.get_args(message)[0])
|
||||
if frycount < 1:
|
||||
raise ValueError
|
||||
except:
|
||||
frycount = 1
|
||||
|
||||
if message.is_reply:
|
||||
reply_message = await message.get_reply_message()
|
||||
data = await check_media(reply_message)
|
||||
|
||||
if isinstance(data, bool):
|
||||
await message.edit("Reply to photo please")
|
||||
return
|
||||
else:
|
||||
await message.edit("Reply to photo please")
|
||||
return
|
||||
|
||||
await message.edit("Downloading...")
|
||||
image = io.BytesIO()
|
||||
await message.client.download_media(data, image)
|
||||
image = Image.open(image)
|
||||
|
||||
await message.edit("Distorting...")
|
||||
for _ in range(frycount):
|
||||
image = await deepfry(image)
|
||||
|
||||
fried_io = io.BytesIO()
|
||||
fried_io.name = "image.jpeg"
|
||||
image.save(fried_io, "JPEG")
|
||||
fried_io.seek(0)
|
||||
await message.delete()
|
||||
await message.reply(file=fried_io)
|
||||
|
||||
|
||||
async def parse(message):
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.edit("<b>Reply to Image!</b>")
|
||||
return None, None
|
||||
args = utils.get_args(message)
|
||||
pix = 100
|
||||
if args:
|
||||
args = args[0]
|
||||
if args.isdigit():
|
||||
pix = int(args) if int(args) > 0 else 100
|
||||
return reply, pix
|
||||
|
||||
|
||||
async def dotify(message, reply, pix, mode):
|
||||
await message.edit("<b>Putting dots...</b>")
|
||||
count = 24
|
||||
im_ = Image.open(io.BytesIO(await reply.download_media(bytes)))
|
||||
if im_.mode == "RGBA":
|
||||
temp = Image.new("RGB", im_.size, "#000")
|
||||
temp.paste(im_, (0, 0), im_)
|
||||
im_ = temp
|
||||
|
||||
im = im_.convert("L")
|
||||
im_ = im if mode else im_
|
||||
[_.thumbnail((pix, pix)) for _ in [im, im_]]
|
||||
w, h = im.size
|
||||
img = Image.new(im_.mode, (w * count + (count // 2), h * count + (count // 2)), 0)
|
||||
ImageDraw.Draw(img)
|
||||
|
||||
def cirsle(im, x, y, r, fill):
|
||||
x += r // 2
|
||||
y += r // 2
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.ellipse((x - r, y - r, x + r, y + r), fill)
|
||||
return im
|
||||
|
||||
_x = _y = count // 2
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
r = im.getpixel((x, y))
|
||||
fill = im_.getpixel((x, y))
|
||||
cirsle(img, _x, _y, r // count, fill)
|
||||
_y += count
|
||||
_x += count
|
||||
_y = count // 2
|
||||
|
||||
out = io.BytesIO()
|
||||
out.name = "out.png"
|
||||
img.save(out)
|
||||
out.seek(0)
|
||||
await reply.reply(file=out)
|
||||
await message.delete()
|
||||
|
||||
|
||||
async def Soaping(file, soap):
|
||||
img = Image.open(io.BytesIO(file))
|
||||
(x, y) = img.size
|
||||
img = img.resize((x // soap, y // soap), Image.ANTIALIAS)
|
||||
img = img.resize((x, y))
|
||||
soap_io = io.BytesIO()
|
||||
soap_io.name = "image.jpeg"
|
||||
img = img.convert("RGB")
|
||||
img.save(soap_io, "JPEG", quality=100)
|
||||
soap_io.seek(0)
|
||||
return soap_io
|
||||
|
||||
|
||||
async def check_media(reply_message):
|
||||
if reply_message and reply_message.media:
|
||||
if reply_message.photo:
|
||||
data = reply_message.photo
|
||||
elif reply_message.document:
|
||||
if (
|
||||
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
|
||||
in reply_message.media.document.attributes
|
||||
):
|
||||
return False
|
||||
if (
|
||||
reply_message.gif
|
||||
or reply_message.video
|
||||
or reply_message.audio
|
||||
or reply_message.voice
|
||||
):
|
||||
return False
|
||||
data = reply_message.media.document
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
if not data or data is None:
|
||||
return False
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
async def KZD(message, type):
|
||||
S = "sticker"
|
||||
A = message
|
||||
N = await A.get_reply_message()
|
||||
Q, J = await CM(N)
|
||||
if not Q or not N:
|
||||
await A.edit("<b>Реплай на стикер или фото!</b>")
|
||||
return
|
||||
O = "KZD." + J
|
||||
P = U.get_args_raw(A)
|
||||
if P:
|
||||
if P in [_A[:A] for A in range(1, len(_A) + 1)]:
|
||||
O = "KZD.png"
|
||||
J = _C
|
||||
if P in [S[:A] for A in range(1, len(S) + 1)]:
|
||||
O = "KZD.webp"
|
||||
J = "webp"
|
||||
R = ist()
|
||||
await A.edit("<b>Извиняюсь...</b>")
|
||||
await A.client.download_media(Q, R)
|
||||
E = Image.open(R)
|
||||
B, C = E.size
|
||||
if B % 2 != 0 and type in [1, 2] or C % 2 != 0 and type in [3, 4]:
|
||||
E = E.resize((B + 1, C + 1))
|
||||
C, B = E.size
|
||||
if type == 1:
|
||||
D = 0
|
||||
F = 0
|
||||
G = B // 2
|
||||
H = C
|
||||
K = G
|
||||
L = D
|
||||
if type == 2:
|
||||
D = B // 2
|
||||
F = 0
|
||||
G = B
|
||||
H = C
|
||||
K = F
|
||||
L = F
|
||||
if type == 3:
|
||||
D = 0
|
||||
F = 0
|
||||
G = B
|
||||
H = C // 2
|
||||
K = D
|
||||
L = H
|
||||
if type == 4:
|
||||
D = 0
|
||||
F = C // 2
|
||||
G = B
|
||||
H = C
|
||||
K = D
|
||||
L = D
|
||||
I = E.crop((D, F, G, H))
|
||||
if type in [1, 2]:
|
||||
I = IO.mirror(I)
|
||||
else:
|
||||
I = IO.flip(I)
|
||||
E.paste(I, (K, L))
|
||||
M = ist()
|
||||
M.name = O
|
||||
E.save(M, J)
|
||||
M.seek(0)
|
||||
await A.client.send_file(A.to_id, M, reply_to=N)
|
||||
await A.delete()
|
||||
|
||||
|
||||
async def CM(R):
|
||||
D = False
|
||||
C = None
|
||||
A = R
|
||||
if A and A.media and A.photo:
|
||||
B = A.photo
|
||||
E = _C
|
||||
elif A and A.media and A.document:
|
||||
if DAF(file_name="AnimatedSticker.tgs") in A.media.document.attributes:
|
||||
return D, C
|
||||
if A.gif or A.video or A.audio or A.voice:
|
||||
return D, C
|
||||
B = A.media.document
|
||||
if _A not in B.mime_type:
|
||||
return D, C
|
||||
E = B.mime_type.split("/")[1]
|
||||
else:
|
||||
return D, C
|
||||
if not B or B is C:
|
||||
return D, C
|
||||
else:
|
||||
return B, E
|
||||
|
||||
|
||||
async def cropping(img):
|
||||
(x, y) = img.size
|
||||
cy = 5
|
||||
cx = 5
|
||||
sx = x // cx
|
||||
sy = y // cy
|
||||
if (sx * cx, sy * cy) != (x, y):
|
||||
img = img.resize((sx * cx, sy * cy))
|
||||
(lx, ly) = (0, 0)
|
||||
media = []
|
||||
for _ in range(1, cy + 1):
|
||||
for o in range(1, cx + 1):
|
||||
mimg = img.crop((lx, ly, lx + sx, ly + sy))
|
||||
mimg = mimg.resize((512, 512))
|
||||
bio = io.BytesIO()
|
||||
bio.name = "image.png"
|
||||
mimg.save(bio, "PNG")
|
||||
media.append(bio.getvalue())
|
||||
lx += sx
|
||||
lx = 0
|
||||
ly += sy
|
||||
return media
|
||||
|
||||
|
||||
async def deepfry(img: Image) -> Image:
|
||||
colours = (
|
||||
(randint(50, 200), randint(40, 170), randint(40, 190)),
|
||||
(randint(190, 255), randint(170, 240), randint(180, 250)),
|
||||
)
|
||||
|
||||
img = img.copy().convert("RGB")
|
||||
|
||||
# Crush image to hell and back
|
||||
img = img.convert("RGB")
|
||||
width, height = img.width, img.height
|
||||
img = img.resize(
|
||||
(int(width ** uniform(0.8, 0.9)), int(height ** uniform(0.8, 0.9))),
|
||||
resample=Image.LANCZOS,
|
||||
)
|
||||
img = img.resize(
|
||||
(int(width ** uniform(0.85, 0.95)), int(height ** uniform(0.85, 0.95))),
|
||||
resample=Image.BILINEAR,
|
||||
)
|
||||
img = img.resize(
|
||||
(int(width ** uniform(0.89, 0.98)), int(height ** uniform(0.89, 0.98))),
|
||||
resample=Image.BICUBIC,
|
||||
)
|
||||
img = img.resize((width, height), resample=Image.BICUBIC)
|
||||
img = ImageOps.posterize(img, randint(3, 7))
|
||||
|
||||
# Generate colour overlay
|
||||
overlay = img.split()[0]
|
||||
overlay = ImageEnhance.Contrast(overlay).enhance(uniform(1.0, 2.0))
|
||||
overlay = ImageEnhance.Brightness(overlay).enhance(uniform(1.0, 2.0))
|
||||
|
||||
overlay = ImageOps.colorize(overlay, colours[0], colours[1])
|
||||
|
||||
# Overlay red and yellow onto main image and sharpen the hell out of it
|
||||
img = Image.blend(img, overlay, uniform(0.1, 0.4))
|
||||
img = ImageEnhance.Sharpness(img).enhance(randint(5, 300))
|
||||
|
||||
return img
|
||||
310
GeekTG/FTG-Modules/information.py
Normal file
310
GeekTG/FTG-Modules/information.py
Normal file
@@ -0,0 +1,310 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @Fl1yd
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from telethon.tl.functions.channels import GetFullChannelRequest, GetParticipantsRequest
|
||||
from telethon.tl.functions.messages import GetHistoryRequest
|
||||
from telethon.tl.functions.photos import GetUserPhotosRequest
|
||||
from telethon.tl.functions.users import GetFullUserRequest
|
||||
from telethon.tl.types import (
|
||||
ChannelParticipantsAdmins,
|
||||
MessageActionChannelMigrateFrom,
|
||||
UserStatusOnline,
|
||||
)
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WhoIsMod(loader.Module):
|
||||
"""Get info about user/chat"""
|
||||
|
||||
strings = {"name": "Information"}
|
||||
|
||||
async def userinfocmd(self, message):
|
||||
"""<@ or reply or id> - info about user"""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
await message.edit("<b>Getting info...</b>")
|
||||
|
||||
try:
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isdigit() else int(args)
|
||||
)
|
||||
else:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
except:
|
||||
user = await message.client.get_me()
|
||||
|
||||
user = await message.client(GetFullUserRequest(user.id))
|
||||
photo, caption = await get_user_info(user, message)
|
||||
|
||||
await message.client.send_file(
|
||||
message.chat_id,
|
||||
photo if photo else None,
|
||||
caption=caption,
|
||||
link_preview=False,
|
||||
reply_to=reply.id if reply else None,
|
||||
)
|
||||
os.remove(photo)
|
||||
await message.delete()
|
||||
|
||||
async def chatinfocmd(self, message):
|
||||
"""<@ or id> - info about chat"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
try:
|
||||
chat = await message.client.get_entity(
|
||||
args if not args.isdigit() else int(args)
|
||||
)
|
||||
except:
|
||||
if not message.is_private:
|
||||
chat = await message.client.get_entity(message.chat_id)
|
||||
else:
|
||||
return await message.edit("<b>It is not a chat!</b>")
|
||||
|
||||
chat = await message.client(GetFullChannelRequest(chat.id))
|
||||
|
||||
await message.edit("<b>Loading info...</b>")
|
||||
|
||||
caption = await get_chat_info(chat, message)
|
||||
|
||||
await message.client.send_message(
|
||||
message.chat_id,
|
||||
str(caption),
|
||||
file=await message.client.download_profile_photo(
|
||||
chat.full_chat.id, "chatphoto.jpg"
|
||||
),
|
||||
)
|
||||
|
||||
await message.delete()
|
||||
|
||||
|
||||
async def get_user_info(user, message):
|
||||
"""Detailed information about the user."""
|
||||
uuser = user.users[0]
|
||||
fulluser = user.full_user
|
||||
|
||||
user_photos = await message.client(
|
||||
GetUserPhotosRequest(user_id=uuser.id, offset=42, max_id=0, limit=100)
|
||||
)
|
||||
user_photos_count = "The user does not have an avatar."
|
||||
try:
|
||||
user_photos_count = user_photos.count
|
||||
except:
|
||||
pass
|
||||
|
||||
user_id = uuser.id
|
||||
first_name = uuser.first_name or "null"
|
||||
last_name = uuser.last_name or "null"
|
||||
username = uuser.username or "null"
|
||||
user_bio = fulluser.about or "null"
|
||||
common_chat = fulluser.common_chats_count
|
||||
is_bot = "Yes" if uuser.bot else "No"
|
||||
restricted = "Yes" if uuser.restricted else "No"
|
||||
verified = "Yes" if uuser.verified else "No"
|
||||
|
||||
photo = await message.client.download_profile_photo(
|
||||
user_id, str(user_id) + ".jpg", download_big=True
|
||||
)
|
||||
caption = (
|
||||
f"<b>USER INFORMATION:</b>\n\n"
|
||||
f"<b>First name:</b> {first_name}\n"
|
||||
f"<b>Last name:</b> {last_name}\n"
|
||||
f"<b>Username:</b> @{username}\n"
|
||||
f"<b>ID:</b> <code>{user_id}</code>\n"
|
||||
f"<b>Bot:</b> {is_bot}\n"
|
||||
f"<b>Restricted:</b> {restricted}\n"
|
||||
f"<b>Verified:</b> {verified}\n\n"
|
||||
f"<b>About:</b> \n<code>{user_bio}</code>\n\n"
|
||||
f"<b>Number of avatars in the profile:</b> {user_photos_count}\n"
|
||||
f"<b>Shared Chats:</b> {common_chat}\n"
|
||||
f'<b>Permalink:</b> <a href="tg://user?id={user_id}">клик</a>'
|
||||
)
|
||||
|
||||
return photo, caption
|
||||
|
||||
|
||||
async def get_chat_info(chat, message):
|
||||
chat_obj_info = await message.client.get_entity(chat.full_chat.id)
|
||||
chat_title = chat_obj_info.title
|
||||
try:
|
||||
msg_info = await message.client(
|
||||
GetHistoryRequest(
|
||||
peer=chat_obj_info.id,
|
||||
offset_id=0,
|
||||
offset_date=datetime(2010, 1, 1),
|
||||
add_offset=-1,
|
||||
limit=1,
|
||||
max_id=0,
|
||||
min_id=0,
|
||||
hash=0,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
msg_info = None
|
||||
|
||||
first_msg_valid = bool(
|
||||
msg_info and msg_info.messages and msg_info.messages[0].id == 1
|
||||
)
|
||||
creator_valid = bool(first_msg_valid and msg_info.users)
|
||||
creator_id = msg_info.users[0].id if creator_valid else None
|
||||
creator_firstname = (
|
||||
msg_info.users[0].first_name
|
||||
if creator_valid and msg_info.users[0].first_name is not None
|
||||
else "УYesлённый аккаунт"
|
||||
)
|
||||
creator_username = (
|
||||
msg_info.users[0].username
|
||||
if creator_valid and msg_info.users[0].username is not None
|
||||
else None
|
||||
)
|
||||
created = msg_info.messages[0].date if first_msg_valid else None
|
||||
former_title = (
|
||||
msg_info.messages[0].action.title
|
||||
if first_msg_valid
|
||||
and type(msg_info.messages[0].action) is MessageActionChannelMigrateFrom
|
||||
and msg_info.messages[0].action.title != chat_title
|
||||
else None
|
||||
)
|
||||
description = chat.full_chat.about
|
||||
members = (
|
||||
chat.full_chat.participants_count
|
||||
if hasattr(chat.full_chat, "participants_count")
|
||||
else chat_obj_info.participants_count
|
||||
)
|
||||
admins = (
|
||||
chat.full_chat.admins_count if hasattr(chat.full_chat, "admins_count") else None
|
||||
)
|
||||
banned_users = (
|
||||
chat.full_chat.kicked_count if hasattr(chat.full_chat, "kicked_count") else None
|
||||
)
|
||||
restrcited_users = (
|
||||
chat.full_chat.banned_count if hasattr(chat.full_chat, "banned_count") else None
|
||||
)
|
||||
users_online = 0
|
||||
async for i in message.client.iter_participants(message.chat_id):
|
||||
if isinstance(i.status, UserStatusOnline):
|
||||
users_online += 1
|
||||
group_stickers = (
|
||||
chat.full_chat.stickerset.title
|
||||
if hasattr(chat.full_chat, "stickerset") and chat.full_chat.stickerset
|
||||
else None
|
||||
)
|
||||
messages_viewable = msg_info.count if msg_info else None
|
||||
messages_sent = (
|
||||
chat.full_chat.read_inbox_max_id
|
||||
if hasattr(chat.full_chat, "read_inbox_max_id")
|
||||
else None
|
||||
)
|
||||
messages_sent_alt = (
|
||||
chat.full_chat.read_outbox_max_id
|
||||
if hasattr(chat.full_chat, "read_outbox_max_id")
|
||||
else None
|
||||
)
|
||||
username = chat_obj_info.username if hasattr(chat_obj_info, "username") else None
|
||||
bots_list = chat.full_chat.bot_info
|
||||
bots = 0
|
||||
slowmode = (
|
||||
"Yes"
|
||||
if hasattr(chat_obj_info, "slowmode_enabled") and chat_obj_info.slowmode_enabled
|
||||
else "No"
|
||||
)
|
||||
slowmode_time = (
|
||||
chat.full_chat.slowmode_seconds
|
||||
if hasattr(chat_obj_info, "slowmode_enabled") and chat_obj_info.slowmode_enabled
|
||||
else None
|
||||
)
|
||||
restricted = (
|
||||
"Yes"
|
||||
if hasattr(chat_obj_info, "restricted") and chat_obj_info.restricted
|
||||
else "No"
|
||||
)
|
||||
verified = (
|
||||
"Yes" if hasattr(chat_obj_info, "verified") and chat_obj_info.verified else "No"
|
||||
)
|
||||
username = "@{}".format(username) if username else None
|
||||
creator_username = "@{}".format(creator_username) if creator_username else None
|
||||
|
||||
if admins is None:
|
||||
try:
|
||||
participants_admins = await message.client(
|
||||
GetParticipantsRequest(
|
||||
channel=chat.full_chat.id,
|
||||
filter=ChannelParticipantsAdmins(),
|
||||
offset=0,
|
||||
limit=0,
|
||||
hash=0,
|
||||
)
|
||||
)
|
||||
admins = participants_admins.count if participants_admins else None
|
||||
except Exception:
|
||||
pass
|
||||
if bots_list:
|
||||
for _ in bots_list:
|
||||
bots += 1
|
||||
|
||||
caption = "<b>CHAT INFORMATION:</b>\n\n"
|
||||
caption += f"<b>ID:</b> {chat_obj_info.id}\n"
|
||||
if chat_title is not None:
|
||||
caption += f"<b>Group name:</b> {chat_title}\n"
|
||||
if former_title is not None:
|
||||
caption += f"<b>Previous name:</b> {former_title}\n"
|
||||
if username is not None:
|
||||
caption += "<b>Group Type:</b> Public\n"
|
||||
caption += f"<b>Link:</b> {username}\n"
|
||||
else:
|
||||
caption += "<b>Group Type:</b> Private\n"
|
||||
if creator_username is not None:
|
||||
caption += f"<b>The Creator:</b> <code>{creator_username}</code>\n"
|
||||
elif creator_valid:
|
||||
caption += f'<b>The Creator:</b> <code><a href="tg://user?id={creator_id}">{creator_firstname}</a></code>\n'
|
||||
if created is not None:
|
||||
caption += f"<b>Created:</b> {created.date().strftime('%b %d, %Y')} - {created.time()}\n"
|
||||
else:
|
||||
caption += f"<b>Created:</b> {chat_obj_info.date.date().strftime('%b %d, %Y')} - {chat_obj_info.date.time()}\n"
|
||||
if messages_viewable is not None:
|
||||
caption += f"<b>Visible messages:</b> {messages_viewable}\n"
|
||||
if messages_sent:
|
||||
caption += f"<b>Total messages:</b> {messages_sent}\n"
|
||||
elif messages_sent_alt:
|
||||
caption += f"<b>Total messages:</b> {messages_sent_alt}\n"
|
||||
if members is not None:
|
||||
caption += f"<b>Participants:</b> {members}\n"
|
||||
if admins is not None:
|
||||
caption += f"<b>Admins:</b> {admins}\n"
|
||||
if bots_list:
|
||||
caption += f"<b>Bots:</b> {bots}\n"
|
||||
if users_online:
|
||||
caption += f"<b>Now Online:</b> {users_online}\n"
|
||||
if restrcited_users is not None:
|
||||
caption += f"<b>Restricted Users:</b> {restrcited_users}\n"
|
||||
if banned_users is not None:
|
||||
caption += f"<b>Banned users:</b> {banned_users}\n"
|
||||
if group_stickers is not None:
|
||||
caption += f'<b>Group stickers:</b> <a href="t.me/addstickers/{chat.full_chat.stickerset.short_name}">{group_stickers}</a>\n'
|
||||
caption += "\n"
|
||||
caption += f"<b>Slowmode:</b> {slowmode}"
|
||||
if hasattr(chat_obj_info, "slowmode_enabled") and chat_obj_info.slowmode_enabled:
|
||||
caption += f", {slowmode_time} seconds\n"
|
||||
else:
|
||||
caption += "\n"
|
||||
caption += f"<b>Restricted:</b> {restricted}\n"
|
||||
if chat_obj_info.restricted:
|
||||
caption += f"> Platform: {chat_obj_info.restriction_reason[0].platform}\n"
|
||||
caption += f"> Reason: {chat_obj_info.restriction_reason[0].reason}\n"
|
||||
caption += f"> Text: {chat_obj_info.restriction_reason[0].text}\n\n"
|
||||
else:
|
||||
caption += ""
|
||||
if hasattr(chat_obj_info, "scam") and chat_obj_info.scam:
|
||||
caption += "<b>Scam</b>: Yes\n\n"
|
||||
if hasattr(chat_obj_info, "verified"):
|
||||
caption += f"<b>Verified:</b> {verified}\n\n"
|
||||
if description:
|
||||
caption += f"<b>Description:</b> \n\n<code>{description}</code>\n"
|
||||
return caption
|
||||
37
GeekTG/FTG-Modules/lmgtfy.py
Normal file
37
GeekTG/FTG-Modules/lmgtfy.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class LMGTFYMod(loader.Module):
|
||||
"""Let me Google that for you, coz you too lazy to do that yourself."""
|
||||
|
||||
strings = {
|
||||
"name": "LetMeGoogleThatForYou",
|
||||
"result": "<b>Here you go, help yourself.</b>\n<a href='{}'>{}</a>",
|
||||
"default": "How to use Google?",
|
||||
}
|
||||
|
||||
@loader.unrestricted
|
||||
async def lmgtfycmd(self, message):
|
||||
"""Use in reply to another message or as .lmgtfy <text>"""
|
||||
text = utils.get_args_raw(message)
|
||||
if not text:
|
||||
if message.is_reply:
|
||||
text = (await message.get_reply_message()).message
|
||||
else:
|
||||
text = self.strings("default", message)
|
||||
query_encoded = urllib.parse.quote_plus(text)
|
||||
lmgtfy_url = "http://lmgtfy.com/?s=g&iie=1&q={}".format(query_encoded)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("result", message).format(
|
||||
utils.escape_html(lmgtfy_url), utils.escape_html(text)
|
||||
),
|
||||
)
|
||||
59
GeekTG/FTG-Modules/lyrics.py
Normal file
59
GeekTG/FTG-Modules/lyrics.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# requires: lyricsgenius
|
||||
|
||||
import logging
|
||||
|
||||
import lyricsgenius
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class LyricsMod(loader.Module):
|
||||
"""Sings songs"""
|
||||
|
||||
strings = {
|
||||
"name": "Lyrics",
|
||||
"genius_api_token_doc": "The LyricsGenius API token from http://genius.com/api-clients",
|
||||
"invalid_syntax": "<b>Please specify song and artist.</b>",
|
||||
"song_not_found": "<b>Song not found</b>",
|
||||
"missing_token": "<b>API Token missing</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"GENIUS_API_TOKEN", None, lambda m: self.strings("genius_api_token_doc", m)
|
||||
)
|
||||
|
||||
def config_complete(self):
|
||||
if self.config["GENIUS_API_TOKEN"]:
|
||||
self.genius = lyricsgenius.Genius(self.config["GENIUS_API_TOKEN"])
|
||||
else:
|
||||
self.genius = None
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def lyricscmd(self, message):
|
||||
""".lyrics Song, Artist"""
|
||||
if self.genius is None:
|
||||
await utils.answer(message, self.strings("missing_token", message))
|
||||
args = utils.get_args_split_by(message, ",")
|
||||
if len(args) != 2:
|
||||
logger.debug(args)
|
||||
await utils.answer(message, self.strings("invalid_syntax", message))
|
||||
return
|
||||
logger.debug("getting song lyrics for " + args[0] + ", " + args[1])
|
||||
try:
|
||||
song = await utils.run_sync(self.genius.search_song, args[0], args[1])
|
||||
except TypeError:
|
||||
# Song not found causes internal library error
|
||||
song = None
|
||||
if song is None:
|
||||
await utils.answer(message, self.strings("song_not_found", message))
|
||||
return
|
||||
logger.debug(song)
|
||||
logger.debug(song.lyrics)
|
||||
await utils.answer(message, utils.escape_html(song.lyrics))
|
||||
6
GeekTG/FTG-Modules/medium.txt
Normal file
6
GeekTG/FTG-Modules/medium.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
information
|
||||
notes
|
||||
purge
|
||||
quotes
|
||||
terminal
|
||||
translate
|
||||
6
GeekTG/FTG-Modules/minimal.txt
Normal file
6
GeekTG/FTG-Modules/minimal.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
information
|
||||
notes
|
||||
purge
|
||||
quotes
|
||||
terminal
|
||||
translate
|
||||
182
GeekTG/FTG-Modules/morze.py
Normal file
182
GeekTG/FTG-Modules/morze.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @trololo_1
|
||||
|
||||
import logging
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class MorzeMod(loader.Module):
|
||||
"""Конвертация текста в шифр Морзе и наоборот.
|
||||
|
||||
Символы использовать не советую, могут возникать ошибки!!"""
|
||||
|
||||
strings = {"name": "Morze"}
|
||||
|
||||
@loader.unrestricted
|
||||
async def tomrzcmd(self, message):
|
||||
""".tomrz [реплай или текст]"""
|
||||
de = {
|
||||
"А": "•- ",
|
||||
"Б": "-••• ",
|
||||
"В": "•-- ",
|
||||
"Г": "--• ",
|
||||
"Д": "-•• ",
|
||||
"Е": "• ",
|
||||
"Ё": "• ",
|
||||
"Ж": "•••- ",
|
||||
"З": "--•• ",
|
||||
"И": "•• ",
|
||||
"Й": "•--- ",
|
||||
"К": "-•- ",
|
||||
"Л": "•-•• ",
|
||||
"М": "-- ",
|
||||
"Н": "-• ",
|
||||
"О": "--- ",
|
||||
"П": "•--• ",
|
||||
"Р": "•-• ",
|
||||
"С": "••• ",
|
||||
"Т": "- ",
|
||||
"У": "••- ",
|
||||
"Ф": "••-• ",
|
||||
"Х": "•••• ",
|
||||
"Ц": "-•-• ",
|
||||
"Ч": "---• ",
|
||||
"Ш": "---- ",
|
||||
"Щ": "--•- ",
|
||||
"Ъ": "--•-- ",
|
||||
"Ы": "-•-- ",
|
||||
"Ь": "-••- ",
|
||||
"Э": "••-•• ",
|
||||
"Ю": "••-- ",
|
||||
"Я": "•-•- ",
|
||||
"1": "•---- ",
|
||||
"2": "••--- ",
|
||||
"3": "•••-- ",
|
||||
"4": "••••- ",
|
||||
"5": "••••• ",
|
||||
"6": "-•••• ",
|
||||
"7": "--••• ",
|
||||
"8": "---•• ",
|
||||
"9": "----• ",
|
||||
"0": "----- ",
|
||||
".": "•••••• ",
|
||||
",": "•-•-•- ",
|
||||
";": "-•-•-• ",
|
||||
":": "---••• ",
|
||||
"?": "••--•• ",
|
||||
"!": "--••-- ",
|
||||
"-": "-••••- ",
|
||||
"(": "-•--• ",
|
||||
")": "-•--•- ",
|
||||
"/": "-••-• ",
|
||||
'"': "•-••-• ",
|
||||
"+": "•-•-• ",
|
||||
"_": "••--•- ",
|
||||
"$": "•••-••- ",
|
||||
"@": "•--•-• ",
|
||||
"=": "-•••- ",
|
||||
"&": "•-••• ",
|
||||
}
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
text = utils.get_args_raw(message)
|
||||
|
||||
if reply and not text:
|
||||
text = reply.raw_text
|
||||
if not text:
|
||||
return await utils.answer(
|
||||
message, "<code>Вы не ввели текст или не сделали реплай.</code>"
|
||||
)
|
||||
x = ""
|
||||
for word in text.split():
|
||||
for letter in word.upper():
|
||||
x += de[letter]
|
||||
x += " "
|
||||
await message.edit(x)
|
||||
|
||||
@loader.unrestricted
|
||||
async def toabccmd(self, message):
|
||||
""".toabc [реплай или текст]"""
|
||||
|
||||
en = {
|
||||
"•-": "А",
|
||||
"-•••": "Б",
|
||||
"•--": "В",
|
||||
"--•": "Г",
|
||||
"-••": "Д",
|
||||
"•": "Е",
|
||||
"•••-": "Ж",
|
||||
"--••": "З",
|
||||
"••": "И",
|
||||
"•---": "Й",
|
||||
"-•-": "К",
|
||||
"•-••": "Л",
|
||||
"--": "М",
|
||||
"-•": "Н",
|
||||
"---": "О",
|
||||
"•--•": "П",
|
||||
"•-•": "Р",
|
||||
"•••": "С",
|
||||
"-": "Т",
|
||||
"••-": "У",
|
||||
"••-•": "Ф",
|
||||
"••••": "Х",
|
||||
"-•-•": "Ц",
|
||||
"---•": "Ч",
|
||||
"----": "Ш",
|
||||
"--•-": "Щ",
|
||||
"--•--": "Ъ",
|
||||
"-•--": "Ы",
|
||||
"-••-": "Ь",
|
||||
"••-••": "Э",
|
||||
"••--": "Ю",
|
||||
"•-•-": "Я",
|
||||
"•----": "1",
|
||||
"••---": "2",
|
||||
"•••--": "3",
|
||||
"••••-": "4",
|
||||
"•••••": "5",
|
||||
"-••••": "6",
|
||||
"--•••": "7",
|
||||
"---••": "8",
|
||||
"----•": "9",
|
||||
"-----": "0",
|
||||
"••••••": ".",
|
||||
"•-•-•-": ",",
|
||||
"-•-•-•": ";",
|
||||
"---•••": ":",
|
||||
"••--••": "?",
|
||||
"--••--": "!",
|
||||
"-••••-": "-",
|
||||
"-•--•-": ")",
|
||||
"-•--•": "(",
|
||||
"-••-•": "/",
|
||||
"•-••-•": '"',
|
||||
"•-•-•": "+",
|
||||
"••--•-": "_",
|
||||
"•••-••-": "$",
|
||||
"•--•-•": "@",
|
||||
"-•••-": "=",
|
||||
"•-•••": "&",
|
||||
}
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
text = utils.get_args_raw(message)
|
||||
|
||||
if reply and not text:
|
||||
text = reply.raw_text
|
||||
if not text:
|
||||
return await utils.answer(
|
||||
message, "<code>Вы не ввели текст или не сделали реплай.</code>"
|
||||
)
|
||||
x = ""
|
||||
for word in text.split(" "):
|
||||
for letter in word.split():
|
||||
x += en[letter].lower()
|
||||
x += " "
|
||||
await message.edit(x)
|
||||
97
GeekTG/FTG-Modules/music.py
Normal file
97
GeekTG/FTG-Modules/music.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: Official Repo, @dekftgmodules
|
||||
|
||||
# requires: lyricsgenius ShazamAPI
|
||||
|
||||
import io
|
||||
|
||||
import lyricsgenius
|
||||
from ShazamAPI import Shazam
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class LyricsMod(loader.Module):
|
||||
"""Sings songs"""
|
||||
|
||||
strings = {
|
||||
"name": "Lyrics",
|
||||
"genius_api_token_doc": "The LyricsGenius API token from http://genius.com/api-clients",
|
||||
"invalid_syntax": "<b>Please specify song and artist.</b>",
|
||||
"song_not_found": "<b>Song not found</b>",
|
||||
"missing_token": "<b>API Token missing</b>",
|
||||
}
|
||||
|
||||
tag = "<b>[Shazam]</b> "
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"GENIUS_API_TOKEN",
|
||||
None,
|
||||
lambda message: self.strings("genius_api_token_doc", message),
|
||||
)
|
||||
|
||||
def config_complete(self):
|
||||
if self.config["GENIUS_API_TOKEN"]:
|
||||
self.genius = lyricsgenius.Genius(self.config["GENIUS_API_TOKEN"])
|
||||
else:
|
||||
self.genius = None
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def lyricscmd(self, message):
|
||||
""".lyrics Song, Artist"""
|
||||
if self.genius is None:
|
||||
await utils.answer(message, self.strings("missing_token", message))
|
||||
args = utils.get_args_split_by(message, ",")
|
||||
if len(args) != 2:
|
||||
await utils.answer(message, self.strings("invalid_syntax", message))
|
||||
return
|
||||
try:
|
||||
song = await utils.run_sync(self.genius.search_song, args[0], args[1])
|
||||
except TypeError:
|
||||
# Song not found causes internal library error
|
||||
song = None
|
||||
if song is None:
|
||||
await utils.answer(message, self.strings("song_not_found", message))
|
||||
return
|
||||
await utils.answer(message, utils.escape_html(song.lyrics))
|
||||
|
||||
async def shazamcmd(self, message):
|
||||
""".shazam <reply to audio> - распознать трек"""
|
||||
s = await get_audio_shazam(message)
|
||||
if not s:
|
||||
return
|
||||
try:
|
||||
shazam = Shazam(s.track.read())
|
||||
recog = shazam.recognizeSong()
|
||||
track = next(recog)[1]["track"]
|
||||
await message.client.send_file(
|
||||
message.to_id,
|
||||
file=track["images"]["background"],
|
||||
caption=self.tag + "Распознанный трек: " + track["share"]["subject"],
|
||||
reply_to=s.reply.id,
|
||||
)
|
||||
await message.delete()
|
||||
except:
|
||||
await message.edit(self.tag + "Не удалось распознать...")
|
||||
|
||||
|
||||
async def get_audio_shazam(message):
|
||||
class rct:
|
||||
track = io.BytesIO()
|
||||
reply = None
|
||||
|
||||
reply = await message.get_reply_message()
|
||||
if reply and reply.file and reply.file.mime_type.split("/")[0] == "audio":
|
||||
ae = rct()
|
||||
await utils.answer(message, "<b>Скачиваю...</b>")
|
||||
ae.track = io.BytesIO(await reply.download_media(bytes))
|
||||
ae.reply = reply
|
||||
await message.edit("<b>Распознаю...</b>")
|
||||
return ae
|
||||
else:
|
||||
await utils.answer(message, "<b>reply to audio...</b>")
|
||||
return None
|
||||
0
GeekTG/FTG-Modules/none.txt
Normal file
0
GeekTG/FTG-Modules/none.txt
Normal file
381
GeekTG/FTG-Modules/noterminal.py
Normal file
381
GeekTG/FTG-Modules/noterminal.py
Normal file
@@ -0,0 +1,381 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @DneZyeK
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import re
|
||||
|
||||
import telethon
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class noTerminalMod(loader.Module):
|
||||
"""Runs commands"""
|
||||
|
||||
strings = {
|
||||
"name": "NoTerminal",
|
||||
"flood_wait_protect_cfg_doc": "How long to wait in seconds between edits in commands",
|
||||
"what_to_kill": "<b>Reply to a terminal command to terminate it</b>",
|
||||
"kill_fail": "<b>Could not kill process</b>",
|
||||
"killed": "<b>Killed</b>",
|
||||
"no_cmd": "<b>No command is running in that message</b>",
|
||||
"running": "<b>Command:</b> <code>{}</code>",
|
||||
"finished": "\n<b>Code:</b> <code>{}</code>",
|
||||
"stdout": "\n<b>Stdout:</b>\n<code>",
|
||||
"stderr": "</code>\n\n<b>Stderr:</b>\n<code>",
|
||||
"end": "</code>",
|
||||
"auth_fail": "<b>Authentication failed, please try again</b>",
|
||||
"auth_needed": '<a href="tg://user?id={}">Interactive authentication required</a>',
|
||||
"auth_msg": (
|
||||
"<b>Please edit this message to the password for</b> "
|
||||
"<code>{}</code> <b>to run</b> <code>{}</code>"
|
||||
),
|
||||
"auth_locked": "<b>Authentication failed, please try again later</b>",
|
||||
"auth_ongoing": "<b>Authenticating...</b>",
|
||||
"done": "<b>Done</b>",
|
||||
"what_note": "<b>What noterminal should be executed?</b>",
|
||||
"no_note": "<b>noteminal not found</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"FLOOD_WAIT_PROTECT",
|
||||
2,
|
||||
lambda m: self.strings("flood_wait_protect_cfg_doc", m),
|
||||
)
|
||||
self.activecmds = {}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
self.db = db
|
||||
self._db = db
|
||||
|
||||
async def noterminalcmd(self, message):
|
||||
"""Gets the note specified"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("what_note", message))
|
||||
return
|
||||
|
||||
asset_id = self.db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0], None
|
||||
)
|
||||
logger.debug(asset_id)
|
||||
if asset_id is not None:
|
||||
asset = await self.db.fetch_asset(asset_id)
|
||||
else:
|
||||
asset_id = self.db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0].lower(), None
|
||||
)
|
||||
asset = (
|
||||
await self.db.fetch_asset(asset_id) if asset_id is not None else None
|
||||
)
|
||||
if asset is None:
|
||||
await utils.answer(message, self.strings("no_note", message))
|
||||
return
|
||||
|
||||
cmd = await self.db.fetch_asset(asset_id)
|
||||
await self.run_command(message, cmd.raw_text)
|
||||
|
||||
async def run_command(self, message, cmd, editor=None):
|
||||
if len(cmd.split(" ")) > 1 and cmd.split(" ")[0] == "sudo":
|
||||
needsswitch = True
|
||||
for word in cmd.split(" ", 1)[1].split(" "):
|
||||
if word[0] != "-":
|
||||
break
|
||||
if word == "-S":
|
||||
needsswitch = False
|
||||
if needsswitch:
|
||||
cmd = " ".join([cmd.split(" ", 1)[0], "-S", cmd.split(" ", 1)[1]])
|
||||
sproc = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=utils.get_base_dir(),
|
||||
)
|
||||
if editor is None:
|
||||
editor = SudoMessageEditor(message, cmd, self.config, self.strings, message)
|
||||
editor.update_process(sproc)
|
||||
self.activecmds[hash_msg(message)] = sproc
|
||||
await editor.redraw()
|
||||
await asyncio.gather(
|
||||
read_stream(
|
||||
editor.update_stdout, sproc.stdout, self.config["FLOOD_WAIT_PROTECT"]
|
||||
),
|
||||
read_stream(
|
||||
editor.update_stderr, sproc.stderr, self.config["FLOOD_WAIT_PROTECT"]
|
||||
),
|
||||
)
|
||||
await editor.cmd_ended(await sproc.wait())
|
||||
del self.activecmds[hash_msg(message)]
|
||||
|
||||
@loader.owner
|
||||
async def noterminatecmd(self, message):
|
||||
"""Use in reply to send SIGTERM to a process"""
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("what_to_kill", message))
|
||||
return
|
||||
if hash_msg(await message.get_reply_message()) in self.activecmds:
|
||||
try:
|
||||
self.activecmds[hash_msg(await message.get_reply_message())].terminate()
|
||||
except Exception:
|
||||
logger.exception("Killing process failed")
|
||||
await utils.answer(message, self.strings("kill_fail", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("killed", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("no_cmd", message))
|
||||
|
||||
@loader.owner
|
||||
async def nokillcmd(self, message):
|
||||
"""Use in reply to send SIGKILL to a process"""
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("what_to_kill", message))
|
||||
return
|
||||
if hash_msg(await message.get_reply_message()) in self.activecmds:
|
||||
try:
|
||||
self.activecmds[hash_msg(await message.get_reply_message())].kill()
|
||||
except Exception:
|
||||
logger.exception("Killing process failed")
|
||||
await utils.answer(message, self.strings("kill_fail", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("killed", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("no_cmd", message))
|
||||
|
||||
|
||||
def hash_msg(message):
|
||||
return f"{utils.get_chat_id(message)}/{message.id}"
|
||||
|
||||
|
||||
async def read_stream(func, stream, delay):
|
||||
last_task = None
|
||||
data = b""
|
||||
while True:
|
||||
dat = await stream.read(1)
|
||||
if not dat:
|
||||
# EOF
|
||||
if last_task:
|
||||
# Send all pending data
|
||||
last_task.cancel()
|
||||
await func(data.decode("utf-8"))
|
||||
# If there is no last task there is inherently no data, so theres no point sending a blank string
|
||||
break
|
||||
data += dat
|
||||
if last_task:
|
||||
last_task.cancel()
|
||||
last_task = asyncio.ensure_future(sleep_for_task(func, data, delay))
|
||||
|
||||
|
||||
async def sleep_for_task(func, data, delay):
|
||||
await asyncio.sleep(delay)
|
||||
await func(data.decode("utf-8"))
|
||||
|
||||
|
||||
class MessageEditor:
|
||||
def __init__(self, message, command, config, strings, request_message):
|
||||
self.message = [message]
|
||||
self.command = command
|
||||
self.stdout = ""
|
||||
self.stderr = ""
|
||||
self.rc = None
|
||||
self.redraws = 0
|
||||
self.config = config
|
||||
self.strings = strings
|
||||
self.request_message = request_message
|
||||
|
||||
async def update_stdout(self, stdout):
|
||||
self.stdout = stdout
|
||||
await self.redraw()
|
||||
|
||||
async def update_stderr(self, stderr):
|
||||
self.stderr = stderr
|
||||
await self.redraw()
|
||||
|
||||
async def redraw(self):
|
||||
text = self.strings("running", self.request_message).format(
|
||||
utils.escape_html(self.command)
|
||||
)
|
||||
if self.rc is not None:
|
||||
text += self.strings("finished", self.request_message).format(
|
||||
utils.escape_html(str(self.rc))
|
||||
)
|
||||
text += self.strings("stdout", self.request_message)
|
||||
text += utils.escape_html(self.stdout[max(len(self.stdout) - 2048, 0) :])
|
||||
text += self.strings("stderr", self.request_message)
|
||||
text += utils.escape_html(self.stderr[max(len(self.stderr) - 1024, 0) :])
|
||||
text += self.strings("end", self.request_message)
|
||||
try:
|
||||
self.message = await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
pass
|
||||
except telethon.errors.rpcerrorlist.MessageTooLongError as e:
|
||||
logger.error(e)
|
||||
logger.error(text)
|
||||
|
||||
# The message is never empty due to the template header
|
||||
|
||||
async def cmd_ended(self, rc):
|
||||
self.rc = rc
|
||||
self.state = 4
|
||||
await self.redraw()
|
||||
|
||||
def update_process(self, process):
|
||||
pass
|
||||
|
||||
|
||||
class SudoMessageEditor(MessageEditor):
|
||||
# Let's just hope these are safe to parse
|
||||
PASS_REQ = "[sudo] password for"
|
||||
WRONG_PASS = r"\[sudo\] password for (.*): Sorry, try again\."
|
||||
TOO_MANY_TRIES = (
|
||||
r"\[sudo\] password for (.*): sudo: [0-9]+ incorrect password attempts"
|
||||
)
|
||||
|
||||
def __init__(self, message, command, config, strings, request_message):
|
||||
super().__init__(message, command, config, strings, request_message)
|
||||
self.process = None
|
||||
self.state = 0
|
||||
self.authmsg = None
|
||||
|
||||
def update_process(self, process):
|
||||
logger.debug("got sproc obj %s", process)
|
||||
self.process = process
|
||||
|
||||
async def update_stderr(self, stderr):
|
||||
logger.debug("stderr update " + stderr)
|
||||
self.stderr = stderr
|
||||
lines = stderr.strip().split("\n")
|
||||
lastline = lines[-1]
|
||||
lastlines = lastline.rsplit(" ", 1)
|
||||
handled = False
|
||||
if (
|
||||
len(lines) > 1
|
||||
and re.fullmatch(self.WRONG_PASS, lines[-2])
|
||||
and lastlines[0] == self.PASS_REQ
|
||||
and self.state == 1
|
||||
):
|
||||
logger.debug("switching state to 0")
|
||||
await self.authmsg.edit(self.strings("auth_failed", self.request_message))
|
||||
self.state = 0
|
||||
handled = True
|
||||
await asyncio.sleep(2)
|
||||
await self.authmsg.delete()
|
||||
if lastlines[0] == self.PASS_REQ and self.state == 0:
|
||||
logger.debug("Success to find sudo log!")
|
||||
text = self.strings("auth_needed", self.request_message).format(
|
||||
(await self.message[0].client.get_me()).id
|
||||
)
|
||||
try:
|
||||
await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError as e:
|
||||
logger.debug(e)
|
||||
logger.debug("edited message with link to self")
|
||||
command = "<code>" + utils.escape_html(self.command) + "</code>"
|
||||
user = utils.escape_html(lastlines[1][:-1])
|
||||
self.authmsg = await self.message[0].client.send_message(
|
||||
"me",
|
||||
self.strings("auth_msg", self.request_message).format(command, user),
|
||||
)
|
||||
logger.debug("sent message to self")
|
||||
self.message[0].client.remove_event_handler(self.on_message_edited)
|
||||
self.message[0].client.add_event_handler(
|
||||
self.on_message_edited,
|
||||
telethon.events.messageedited.MessageEdited(chats=["me"]),
|
||||
)
|
||||
logger.debug("registered handler")
|
||||
handled = True
|
||||
if len(lines) > 1 and (
|
||||
re.fullmatch(self.TOO_MANY_TRIES, lastline)
|
||||
and (self.state == 1 or self.state == 3 or self.state == 4)
|
||||
):
|
||||
logger.debug("password wrong lots of times")
|
||||
await utils.answer(
|
||||
self.message, self.strings("auth_locked", self.request_message)
|
||||
)
|
||||
await self.authmsg.delete()
|
||||
self.state = 2
|
||||
handled = True
|
||||
if not handled:
|
||||
logger.debug("Didn't find sudo log.")
|
||||
if self.authmsg is not None:
|
||||
await self.authmsg[0].delete()
|
||||
self.authmsg = None
|
||||
self.state = 2
|
||||
await self.redraw()
|
||||
logger.debug(self.state)
|
||||
|
||||
async def update_stdout(self, stdout):
|
||||
self.stdout = stdout
|
||||
if self.state != 2:
|
||||
self.state = 3 # Means that we got stdout only
|
||||
if self.authmsg is not None:
|
||||
await self.authmsg.delete()
|
||||
self.authmsg = None
|
||||
await self.redraw()
|
||||
|
||||
async def on_message_edited(self, message):
|
||||
# Message contains sensitive information.
|
||||
if self.authmsg is None:
|
||||
return
|
||||
logger.debug(f"got message edit update in self {message.id}")
|
||||
if hash_msg(message) == hash_msg(self.authmsg):
|
||||
# The user has provided interactive authentication. Send password to stdin for sudo.
|
||||
try:
|
||||
self.authmsg = await utils.answer(
|
||||
message, self.strings("auth_ongoing", self.request_message)
|
||||
)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
# Try to clear personal info if the edit fails
|
||||
await message.delete()
|
||||
self.state = 1
|
||||
self.process.stdin.write(
|
||||
message.message.message.split("\n", 1)[0].encode("utf-8") + b"\n"
|
||||
)
|
||||
|
||||
|
||||
class RawMessageEditor(SudoMessageEditor):
|
||||
def __init__(
|
||||
self, message, command, config, strings, request_message, show_done=False
|
||||
):
|
||||
super().__init__(message, command, config, strings, request_message)
|
||||
self.show_done = show_done
|
||||
|
||||
async def redraw(self):
|
||||
logger.debug(self.rc)
|
||||
if self.rc is None:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stdout[max(len(self.stdout) - 4095, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
elif self.rc == 0:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stdout[max(len(self.stdout) - 4090, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
else:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stderr[max(len(self.stderr) - 4095, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
if self.rc is not None and self.show_done:
|
||||
text += "\n" + self.strings("done", self.request_message)
|
||||
logger.debug(text)
|
||||
try:
|
||||
await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
pass
|
||||
except (telethon.errors.rpcerrorlist.MessageEmptyError, ValueError):
|
||||
pass
|
||||
except telethon.errors.rpcerrorlist.MessageTooLongError as e:
|
||||
logger.error(e)
|
||||
logger.error(text)
|
||||
140
GeekTG/FTG-Modules/notes.py
Normal file
140
GeekTG/FTG-Modules/notes.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger("friendly-telegram.modules.notes")
|
||||
|
||||
|
||||
@loader.tds
|
||||
class NotesMod(loader.Module):
|
||||
"""Stores global notes (aka snips)"""
|
||||
|
||||
strings = {
|
||||
"name": "Notes",
|
||||
"what_note": "<b>What note should I show?</b>",
|
||||
"no_note": "<b>Note not found</b>",
|
||||
"save_what": "<b>And what to save?</b>",
|
||||
"what_name": "<b>And what will the note be called?</b>",
|
||||
"saved": "<b>Note saved as:</b> <code>{}</code>",
|
||||
"notes_header": "<b>Saved Notes:</b>\n\n",
|
||||
"notes_item": "<b>▷</b> <code>{}</code>",
|
||||
"delnote_args": "<b>And what note should I delete?</b>",
|
||||
"delnote_done": "<b>Note deleted!</b>",
|
||||
"delnotes_none": "<b>And there are no notes...</b>",
|
||||
"delnotes_done": "<b>ALL NOTES DELETED</b>",
|
||||
"notes_none": "<b>And there are no notes...</b>",
|
||||
}
|
||||
|
||||
async def findnotecmd(self, message):
|
||||
"""Gets the note specified"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("what_note", message))
|
||||
return
|
||||
asset_id = self._db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0], None
|
||||
)
|
||||
logger.debug(asset_id)
|
||||
asset = await self._db.fetch_asset(asset_id) if asset_id is not None else None
|
||||
if asset is None:
|
||||
self.del_note(args[0])
|
||||
await utils.answer(message, self.strings("no_note", message))
|
||||
return
|
||||
link = "https://t.me/c/{}/{}".format(asset.chat.id, asset.id)
|
||||
await message.edit(
|
||||
f'<b>Заметка</b> "<code>{args[0]}</code>" <a href="{link}">находится здесь.</a>'
|
||||
)
|
||||
|
||||
async def notecmd(self, message):
|
||||
"""Gets the note specified"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("what_note", message))
|
||||
return
|
||||
asset_id = self._db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0], None
|
||||
)
|
||||
logger.debug(asset_id)
|
||||
asset = await self._db.fetch_asset(asset_id) if asset_id is not None else None
|
||||
if asset is None:
|
||||
self.del_note(args[0])
|
||||
await utils.answer(message, self.strings("no_note", message))
|
||||
return
|
||||
if message.out:
|
||||
await message.delete()
|
||||
await message.client.send_message(
|
||||
message.chat_id,
|
||||
await self._db.fetch_asset(asset_id),
|
||||
reply_to=await message.get_reply_message(),
|
||||
)
|
||||
|
||||
async def delallnotescmd(self, message):
|
||||
"""Deletes all the saved notes"""
|
||||
if not self._db.get("friendly-telegram.modules.notes", "notes", {}):
|
||||
await utils.answer(message, self.strings("delnotes_none", message))
|
||||
return
|
||||
self._db.get("friendly-telegram.modules.notes", "notes", {}).clear()
|
||||
await utils.answer(message, self.strings("delnotes_done", message))
|
||||
|
||||
async def savecmd(self, message):
|
||||
"""Save a new note. Must be used in reply with one parameter (note name)"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("what_name", message))
|
||||
return
|
||||
if message.is_reply:
|
||||
target = await message.get_reply_message()
|
||||
elif len(args) < 2:
|
||||
await utils.answer(message, self.strings("save_what", message))
|
||||
return
|
||||
else:
|
||||
message.entities = None
|
||||
message.message = args[1]
|
||||
target = message
|
||||
logger.debug(target.message)
|
||||
asset_id = await self._db.store_asset(target)
|
||||
self._db.set(
|
||||
"friendly-telegram.modules.notes",
|
||||
"notes",
|
||||
{
|
||||
**self._db.get("friendly-telegram.modules.notes", "notes", {}),
|
||||
args[0]: asset_id,
|
||||
},
|
||||
)
|
||||
await utils.answer(message, str(self.strings("saved", message)).format(args[0]))
|
||||
|
||||
async def delnotecmd(self, message):
|
||||
"""Deletes a note, specified by note name"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("delnote_args", message))
|
||||
self.del_note(args[0])
|
||||
await utils.answer(message, self.strings("delnote_done", message))
|
||||
|
||||
def del_note(self, note):
|
||||
old = self._db.get("friendly-telegram.modules.notes", "notes", {})
|
||||
try:
|
||||
del old[note]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
self._db.set("friendly-telegram.modules.notes", "notes", old)
|
||||
|
||||
async def notescmd(self, message):
|
||||
"""List the saved notes"""
|
||||
if not self._db.get("friendly-telegram.modules.notes", "notes", {}):
|
||||
await utils.answer(message, self.strings("notes_none", message))
|
||||
return
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("notes_header", message)
|
||||
+ "\n".join(
|
||||
self.strings("notes_item", message).format(key)
|
||||
for key in self._db.get("friendly-telegram.modules.notes", "notes", {})
|
||||
),
|
||||
)
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._db = db
|
||||
121
GeekTG/FTG-Modules/notexec.py
Normal file
121
GeekTG/FTG-Modules/notexec.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @DneZyeK
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
|
||||
import telethon
|
||||
from meval import meval
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ExecutorMod(loader.Module):
|
||||
"""Stores global notes (aka snips)"""
|
||||
|
||||
strings = {
|
||||
"name": "Notexec",
|
||||
"what_note": "<b>What notexec should be executed?</b>",
|
||||
"no_note": "<b>Notexec not found</b>",
|
||||
"execute_fail": ("<b>Failed to execute expression:</b>\n<code>{}</code>"),
|
||||
}
|
||||
|
||||
async def notexeccmd(self, message):
|
||||
"""Gets the note specified"""
|
||||
args = utils.get_args(message)
|
||||
if not args:
|
||||
await utils.answer(message, self.strings("what_note", message))
|
||||
return
|
||||
|
||||
asset_id = self._db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0], None
|
||||
)
|
||||
logger.debug(asset_id)
|
||||
if asset_id is not None:
|
||||
asset = await self._db.fetch_asset(asset_id)
|
||||
else:
|
||||
asset_id = self._db.get("friendly-telegram.modules.notes", "notes", {}).get(
|
||||
args[0].lower(), None
|
||||
)
|
||||
asset = (
|
||||
await self._db.fetch_asset(asset_id) if asset_id is not None else None
|
||||
)
|
||||
if asset is None:
|
||||
await utils.answer(message, self.strings("no_note", message))
|
||||
return
|
||||
|
||||
cmd = await self._db.fetch_asset(asset_id)
|
||||
|
||||
try:
|
||||
await meval(cmd.raw_text, globals(), **await self.getattrs(message))
|
||||
except Exception:
|
||||
exc = sys.exc_info()
|
||||
exc = "".join(
|
||||
traceback.format_exception(
|
||||
exc[0], exc[1], exc[2].tb_next.tb_next.tb_next
|
||||
)
|
||||
)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("execute_fail", message).format(utils.escape_html(exc)),
|
||||
)
|
||||
return
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
self.db = db
|
||||
self._db = db
|
||||
|
||||
async def getattrs(self, message):
|
||||
return {
|
||||
"message": message,
|
||||
"client": self.client,
|
||||
"self": self,
|
||||
"db": self.db,
|
||||
"reply": await message.get_reply_message(),
|
||||
"event": message,
|
||||
"chat": message.to_id,
|
||||
**self.get_types(),
|
||||
**self.get_functions(),
|
||||
}
|
||||
|
||||
def get_types(self):
|
||||
return self.get_sub(telethon.tl.types)
|
||||
|
||||
def get_functions(self):
|
||||
return self.get_sub(telethon.tl.functions)
|
||||
|
||||
def get_sub(self, it, _depth=1):
|
||||
"""Get all callable capitalised objects in an object recursively, ignoring _*"""
|
||||
return {
|
||||
**dict(
|
||||
filter(
|
||||
lambda x: x[0][0] != "_"
|
||||
and x[0][0].upper() == x[0][0]
|
||||
and callable(x[1]),
|
||||
it.__dict__.items(),
|
||||
)
|
||||
),
|
||||
**dict(
|
||||
itertools.chain.from_iterable(
|
||||
[
|
||||
self.get_sub(y[1], _depth + 1).items()
|
||||
for y in filter(
|
||||
lambda x: x[0][0] != "_"
|
||||
and isinstance(x[1], types.ModuleType)
|
||||
and x[1] != it
|
||||
and x[1].__package__.rsplit(".", _depth)[0]
|
||||
== "telethon.tl",
|
||||
it.__dict__.items(),
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
}
|
||||
75
GeekTG/FTG-Modules/pmlog.py
Normal file
75
GeekTG/FTG-Modules/pmlog.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from telethon import types
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class PMLogMod(loader.Module):
|
||||
"""Logs unwanted PMs to a channel"""
|
||||
|
||||
strings = {
|
||||
"name": "PM Logger",
|
||||
"start": "<b>Your conversation is now being logged</b>",
|
||||
"not_pm": "<b>You can't log a group</b>",
|
||||
"stopped": "<b>Your conversation is no longer being logged</b>",
|
||||
"log_group_cfg_doc": "Group or channel ID where to send the logged PMs",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"LOG_GROUP", None, lambda m: self.strings("log_group_cfg_doc", m)
|
||||
)
|
||||
|
||||
async def logpmcmd(self, message):
|
||||
"""Begins logging PMs"""
|
||||
if (
|
||||
not message.is_private
|
||||
or message.to_id.user_id == (await message.client.get_me(True)).user_id
|
||||
):
|
||||
await utils.answer(message, self.strings("not_pm", message))
|
||||
return
|
||||
self._db.set(
|
||||
__name__,
|
||||
"users",
|
||||
list(set(self._db.get(__name__, "users", []) + [message.to_id.user_id])),
|
||||
)
|
||||
msgs = await utils.answer(message, self.strings("start", message))
|
||||
await asyncio.sleep(1)
|
||||
await message.client.delete_messages(message.to_id, msgs)
|
||||
|
||||
async def unlogpmcmd(self, message):
|
||||
"""Stops logging PMs"""
|
||||
if (
|
||||
not message.is_private
|
||||
or message.to_id.user_id == (await message.client.get_me(True)).user_id
|
||||
):
|
||||
await utils.answer(message, self.strings("not_pm", message))
|
||||
return
|
||||
self._db.set(
|
||||
__name__,
|
||||
"users",
|
||||
list(
|
||||
set(self._db.get(__name__, "users", [])).difference(
|
||||
[message.to_id.user_id]
|
||||
)
|
||||
),
|
||||
)
|
||||
await utils.answer(message, self.strings("stopped", message))
|
||||
|
||||
async def watcher(self, message):
|
||||
if not message.is_private or not isinstance(message, types.Message):
|
||||
return
|
||||
if self.config["LOG_GROUP"] and utils.get_chat_id(message) in self._db.get(
|
||||
__name__, "users", []
|
||||
):
|
||||
await message.forward_to(self.config["LOG_GROUP"])
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._db = db
|
||||
91
GeekTG/FTG-Modules/purge.py
Normal file
91
GeekTG/FTG-Modules/purge.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
import telethon
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class PurgeMod(loader.Module):
|
||||
"""Deletes your messages"""
|
||||
|
||||
strings = {
|
||||
"name": "Purge",
|
||||
"from_where": "<b>Which messages should be purged?</b>",
|
||||
"not_supergroup_bot": "<b>Purges can only take place in supergroups</b>",
|
||||
"delete_what": "<b>What message should be deleted?</b>",
|
||||
}
|
||||
|
||||
@loader.group_admin_delete_messages
|
||||
@loader.ratelimit
|
||||
async def purgecmd(self, message):
|
||||
"""Purge from the replied message"""
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("from_where", message))
|
||||
return
|
||||
from_users = set()
|
||||
args = utils.get_args(message)
|
||||
for arg in args:
|
||||
try:
|
||||
try:
|
||||
arg = int(arg)
|
||||
except:
|
||||
pass
|
||||
entity = await message.client.get_entity(arg)
|
||||
if isinstance(entity, telethon.tl.types.User):
|
||||
from_users.add(entity.id)
|
||||
except ValueError:
|
||||
pass
|
||||
msgs = []
|
||||
from_ids = set()
|
||||
if await message.client.is_bot():
|
||||
if not message.is_channel:
|
||||
await utils.answer(message, self.strings("not_supergroup_bot", message))
|
||||
return
|
||||
for msg in range(message.reply_to_msg_id, message.id + 1):
|
||||
msgs.append(msg)
|
||||
if len(msgs) >= 99:
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(message.to_id, msgs)
|
||||
msgs.clear()
|
||||
else:
|
||||
async for msg in message.client.iter_messages(
|
||||
entity=message.to_id, min_id=message.reply_to_msg_id - 1, reverse=True
|
||||
):
|
||||
if from_users and msg.sender_id not in from_users:
|
||||
continue
|
||||
msgs.append(msg.id)
|
||||
from_ids.add(msg.sender_id)
|
||||
if len(msgs) >= 99:
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(message.to_id, msgs)
|
||||
msgs.clear()
|
||||
if msgs:
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(message.to_id, msgs)
|
||||
await self.allmodules.log("purge", group=message.to_id, affected_uids=from_ids)
|
||||
|
||||
@loader.group_admin_delete_messages
|
||||
@loader.ratelimit
|
||||
async def delcmd(self, message):
|
||||
"""Delete the replied message"""
|
||||
msgs = [message.id]
|
||||
if not message.is_reply:
|
||||
if await message.client.is_bot():
|
||||
await utils.answer(message, self.strings("delete_what", message))
|
||||
return
|
||||
msg = await message.client.iter_messages(
|
||||
message.to_id, 1, max_id=message.id
|
||||
).__anext__()
|
||||
else:
|
||||
msg = await message.get_reply_message()
|
||||
msgs.append(msg.id)
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(message.to_id, msgs)
|
||||
await self.allmodules.log(
|
||||
"delete", group=message.to_id, affected_uids=[msg.sender_id]
|
||||
)
|
||||
93
GeekTG/FTG-Modules/qr_code.py
Normal file
93
GeekTG/FTG-Modules/qr_code.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
from requests import get, post
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class QRtoolsMod(loader.Module):
|
||||
"""Generator and reader of QR codes"""
|
||||
|
||||
strings = {"name": "QR Code"}
|
||||
|
||||
@loader.owner
|
||||
async def makeqrcmd(self, message):
|
||||
""".makeqr <text or reply>"""
|
||||
text = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
file = False
|
||||
if not text or text.lower() == ".file":
|
||||
if text and text == ".file":
|
||||
file = True
|
||||
if not reply or not reply.message:
|
||||
await message.edit("<b>Нет текста для кодирования!</b>")
|
||||
return
|
||||
text = reply.raw_text
|
||||
else:
|
||||
if text.startswith(".file"):
|
||||
file = True
|
||||
text = text[5:].strip()
|
||||
url = "https://api.qrserver.com/v1/create-qr-code/?data={}&size=512x512&charset-source=UTF-8&charset-target=UTF-8&ecc=L&color=0-0-0&bgcolor=255-255-255&margin=1&qzone=1&format=png"
|
||||
r = get(url.format(text), stream=True)
|
||||
qrcode = BytesIO()
|
||||
qrcode.name = "qr.png" if file else "qr.webp"
|
||||
Image.open(BytesIO(r.content)).save(qrcode)
|
||||
qrcode.seek(0)
|
||||
await message.delete()
|
||||
await message.client.send_file(
|
||||
message.to_id, qrcode, reply_to=reply, force_document=file
|
||||
)
|
||||
|
||||
@loader.owner
|
||||
async def readqrcmd(self, message):
|
||||
""".readqr <qrcode or reply to qrcode>"""
|
||||
ok = await check(message)
|
||||
if not ok:
|
||||
reply = await message.get_reply_message()
|
||||
ok = await check(reply)
|
||||
if not ok:
|
||||
text = (
|
||||
"<b>Это не изображение!</b>" if reply else "<b>Нечего не передано!</b>"
|
||||
)
|
||||
await message.edit(text)
|
||||
return
|
||||
file = BytesIO()
|
||||
file.name = "qr.png"
|
||||
data = await message.client.download_file(ok)
|
||||
Image.open(BytesIO(data)).save(file)
|
||||
url = "https://api.qrserver.com/v1/read-qr-code/?outputformat=json"
|
||||
resp = post(url, files={"file": file.getvalue()})
|
||||
text = resp.json()[0]["symbol"][0]["data"]
|
||||
if not text:
|
||||
text = "<b>Невозможно распознать или QR пуст!<b>"
|
||||
await utils.answer(message, text)
|
||||
|
||||
|
||||
async def check(msg):
|
||||
if msg and msg.media:
|
||||
if msg.photo:
|
||||
ok = msg.photo
|
||||
elif msg.document:
|
||||
if (
|
||||
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
|
||||
in msg.media.document.attributes
|
||||
):
|
||||
return False
|
||||
if msg.gif or msg.video or msg.audio or msg.voice:
|
||||
return False
|
||||
ok = msg.media.document
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
if not ok or ok is None:
|
||||
return False
|
||||
else:
|
||||
return ok
|
||||
484
GeekTG/FTG-Modules/quotes.py
Normal file
484
GeekTG/FTG-Modules/quotes.py
Normal file
@@ -0,0 +1,484 @@
|
||||
# API & module author: @mishase
|
||||
|
||||
# requires: requests Pillow cryptg
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
|
||||
import PIL
|
||||
import requests
|
||||
from telethon import utils
|
||||
from telethon.tl.types import (
|
||||
ChannelParticipantCreator,
|
||||
ChannelParticipantsAdmins,
|
||||
ChatPhotoEmpty,
|
||||
DocumentAttributeSticker,
|
||||
Message,
|
||||
MessageEntityBold,
|
||||
MessageEntityBotCommand,
|
||||
MessageEntityCashtag,
|
||||
MessageEntityCode,
|
||||
MessageEntityHashtag,
|
||||
MessageEntityItalic,
|
||||
MessageEntityMention,
|
||||
MessageEntityMentionName,
|
||||
MessageEntityPhone,
|
||||
MessageEntityStrike,
|
||||
MessageEntityTextUrl,
|
||||
MessageEntityUnderline,
|
||||
MessageEntityUrl,
|
||||
MessageMediaDocument,
|
||||
MessageMediaPhoto,
|
||||
MessageMediaWebPage,
|
||||
PeerBlocked,
|
||||
PeerChannel,
|
||||
PeerChat,
|
||||
PeerUser,
|
||||
User,
|
||||
)
|
||||
|
||||
from .. import loader
|
||||
from .. import utils as ftgUtils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
null = None
|
||||
false = False
|
||||
true = True
|
||||
|
||||
PIL.Image.MAX_IMAGE_PIXELS = null
|
||||
|
||||
|
||||
class dict(dict):
|
||||
def __setattr__(self, attr, value):
|
||||
self[attr] = value
|
||||
|
||||
|
||||
BUILD_ID = "96b0cc3e-dca7-4b79-8db2-43a478b00f9a" # null to disable autoupdates
|
||||
MODULE_PATH = "https://quotes.mishase.dev/f/module.py"
|
||||
|
||||
|
||||
@loader.tds
|
||||
class mQuotesMod(loader.Module):
|
||||
"""Quote a message using Mishase Quotes API"""
|
||||
|
||||
strings = {"name": "Quotes"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"QUOTE_MESSAGES_LIMIT",
|
||||
50,
|
||||
"Messages limit",
|
||||
"MAX_WIDTH",
|
||||
384,
|
||||
"Max width (px)",
|
||||
"SCALE_FACTOR",
|
||||
5,
|
||||
"Scale factor",
|
||||
"SQUARE_AVATAR",
|
||||
false,
|
||||
"Square avatar",
|
||||
"TEXT_COLOR",
|
||||
"white",
|
||||
"Text color",
|
||||
"REPLY_LINE_COLOR",
|
||||
"white",
|
||||
"Reply line color",
|
||||
"REPLY_THUMB_BORDER_RADIUS",
|
||||
2,
|
||||
"Reply thumbnail radius (px)",
|
||||
"ADMINTITLE_COLOR",
|
||||
"#969ba0",
|
||||
"Admin title color",
|
||||
"MESSAGE_BORDER_RADIUS",
|
||||
10,
|
||||
"Message radius (px)",
|
||||
"PICTURE_BORDER_RADIUS",
|
||||
8,
|
||||
"Picture radius (px)",
|
||||
"BACKGROUND_COLOR",
|
||||
"#162330",
|
||||
"Background color",
|
||||
)
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def quotecmd(self, msg):
|
||||
"""Quote a message. Args: ?<count> ?file"""
|
||||
args = ftgUtils.get_args_raw(msg)
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if not reply:
|
||||
return await msg.edit("No reply message")
|
||||
|
||||
if not msg.out:
|
||||
msg = await msg.reply("_")
|
||||
|
||||
count = 1
|
||||
forceDocument = false
|
||||
|
||||
if args:
|
||||
args = args.split()
|
||||
forceDocument = "file" in args
|
||||
try:
|
||||
count = next(int(arg) for arg in args if arg.isdigit())
|
||||
count = max(1, min(self.config["QUOTE_MESSAGES_LIMIT"], count))
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
messagePacker = MessagePacker(self.client)
|
||||
|
||||
if count == 1:
|
||||
await msg.edit("<b>Processing...</b>")
|
||||
await messagePacker.add(reply)
|
||||
if count > 1:
|
||||
it = self.client.iter_messages(
|
||||
reply.peer_id,
|
||||
offset_id=reply.id,
|
||||
reverse=true,
|
||||
add_offset=1,
|
||||
limit=count,
|
||||
)
|
||||
|
||||
i = 1
|
||||
async for message in it:
|
||||
await msg.edit(f"<b>Processing {i}/{count}</b>")
|
||||
i += 1
|
||||
await messagePacker.add(message)
|
||||
|
||||
messages = messagePacker.messages
|
||||
|
||||
if not messages:
|
||||
return await msg.edit("No messages to quote")
|
||||
|
||||
files = []
|
||||
for f in messagePacker.files.values():
|
||||
files.append(("files", f))
|
||||
|
||||
if not files:
|
||||
files.append(("files", bytearray()))
|
||||
|
||||
await msg.edit("<b>API Processing...</b>")
|
||||
|
||||
resp = await ftgUtils.run_sync(
|
||||
requests.post,
|
||||
"https://quotes.mishase.dev/create",
|
||||
data={
|
||||
"data": json.dumps(
|
||||
{
|
||||
"messages": messages,
|
||||
"maxWidth": self.config["MAX_WIDTH"],
|
||||
"scaleFactor": self.config["SCALE_FACTOR"],
|
||||
"squareAvatar": self.config["SQUARE_AVATAR"],
|
||||
"textColor": self.config["TEXT_COLOR"],
|
||||
"replyLineColor": self.config["REPLY_LINE_COLOR"],
|
||||
"adminTitleColor": self.config["ADMINTITLE_COLOR"],
|
||||
"messageBorderRadius": self.config["MESSAGE_BORDER_RADIUS"],
|
||||
"replyThumbnailBorderRadius": self.config[
|
||||
"REPLY_THUMB_BORDER_RADIUS"
|
||||
],
|
||||
"pictureBorderRadius": self.config["PICTURE_BORDER_RADIUS"],
|
||||
"backgroundColor": self.config["BACKGROUND_COLOR"],
|
||||
}
|
||||
),
|
||||
"moduleBuild": BUILD_ID,
|
||||
},
|
||||
files=files,
|
||||
timeout=99,
|
||||
)
|
||||
|
||||
if resp.status_code == 418:
|
||||
if await update(self.allmodules.modules, msg):
|
||||
await self.allmodules.commands["quote"](msg)
|
||||
else:
|
||||
await msg.edit("<b>Update error</b>")
|
||||
return
|
||||
|
||||
await msg.edit("<b>Sending...</b>")
|
||||
|
||||
image = io.BytesIO()
|
||||
image.name = "quote.webp"
|
||||
|
||||
PIL.Image.open(io.BytesIO(resp.content)).save(image, "WEBP")
|
||||
image.seek(0)
|
||||
|
||||
await self.client.send_message(
|
||||
msg.peer_id, file=image, force_document=forceDocument
|
||||
)
|
||||
|
||||
await msg.delete()
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def fquotecmd(self, msg):
|
||||
"""Fake message quote. Args: @<username>/<id>/<reply> <text>"""
|
||||
args = ftgUtils.get_args_raw(msg)
|
||||
reply = await msg.get_reply_message()
|
||||
splitArgs = args.split(maxsplit=1)
|
||||
if len(splitArgs) == 2 and (
|
||||
splitArgs[0].startswith("@") or splitArgs[0].isdigit()
|
||||
):
|
||||
user = (
|
||||
splitArgs[0][1:] if splitArgs[0].startswith("@") else int(splitArgs[0])
|
||||
)
|
||||
text = splitArgs[1]
|
||||
elif reply:
|
||||
user = reply.sender_id
|
||||
text = args
|
||||
else:
|
||||
return await msg.edit("Incorrect args")
|
||||
|
||||
try:
|
||||
uid = (await self.client.get_entity(user)).id
|
||||
except Exception:
|
||||
return await msg.edit("User not found")
|
||||
|
||||
async def getMessage():
|
||||
return Message(0, uid, message=text)
|
||||
|
||||
msg.message = ""
|
||||
msg.get_reply_message = getMessage
|
||||
|
||||
await self.quotecmd(msg)
|
||||
|
||||
|
||||
class MessagePacker:
|
||||
def __init__(self, client):
|
||||
self.files = dict()
|
||||
self.messages = []
|
||||
self.client = client
|
||||
|
||||
async def add(self, msg):
|
||||
packed = await self.packMessage(msg)
|
||||
if packed:
|
||||
self.messages.append(packed)
|
||||
|
||||
async def packMessage(self, msg):
|
||||
obj = dict()
|
||||
|
||||
text = msg.message
|
||||
if text:
|
||||
obj.text = text
|
||||
|
||||
entities = MessagePacker.encodeEntities(msg.entities or [])
|
||||
if entities:
|
||||
obj.entities = entities
|
||||
|
||||
media = msg.media
|
||||
if media:
|
||||
file = await self.downloadMedia(media)
|
||||
if file:
|
||||
obj.picture = {"file": file}
|
||||
|
||||
if "text" not in obj and "picture" not in obj:
|
||||
return null
|
||||
|
||||
obj.author = await self.encodeAuthor(msg)
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
if reply:
|
||||
obj.reply = await self.encodeReply(reply)
|
||||
|
||||
return obj
|
||||
|
||||
def encodeEntities(entities):
|
||||
encEntities = []
|
||||
for entity in entities:
|
||||
entityType = MessagePacker.getEntityType(entity)
|
||||
if entityType:
|
||||
encEntities.append(
|
||||
{
|
||||
"type": entityType,
|
||||
"offset": entity.offset,
|
||||
"length": entity.length,
|
||||
}
|
||||
)
|
||||
return encEntities
|
||||
|
||||
def getEntityType(entity):
|
||||
t = type(entity)
|
||||
if t is MessageEntityBold:
|
||||
return "bold"
|
||||
if t is MessageEntityItalic:
|
||||
return "italic"
|
||||
if t in [MessageEntityUrl, MessageEntityPhone]:
|
||||
return "url"
|
||||
if t is MessageEntityCode:
|
||||
return "monospace"
|
||||
if t is MessageEntityStrike:
|
||||
return "strikethrough"
|
||||
if t is MessageEntityUnderline:
|
||||
return "underline"
|
||||
if t in [
|
||||
MessageEntityMention,
|
||||
MessageEntityTextUrl,
|
||||
MessageEntityMentionName,
|
||||
MessageEntityHashtag,
|
||||
MessageEntityCashtag,
|
||||
MessageEntityBotCommand,
|
||||
]:
|
||||
return "bluetext"
|
||||
return null
|
||||
|
||||
async def downloadMedia(self, inMedia, thumb=null):
|
||||
media = MessagePacker.getMedia(inMedia)
|
||||
if not media:
|
||||
return null
|
||||
mid = str(media.id)
|
||||
if thumb:
|
||||
mid += "." + str(thumb)
|
||||
if mid not in self.files:
|
||||
try:
|
||||
mime = media.mime_type
|
||||
except AttributeError:
|
||||
mime = "image/jpg"
|
||||
dl = await self.client.download_media(media, bytes, thumb=thumb)
|
||||
self.files[mid] = (str(len(self.files)), dl, mime)
|
||||
return self.files[mid][0]
|
||||
|
||||
def getMedia(media):
|
||||
t = type(media)
|
||||
if t is MessageMediaPhoto:
|
||||
return media.photo
|
||||
if t is MessageMediaDocument:
|
||||
for attribute in media.document.attributes:
|
||||
if isinstance(attribute, DocumentAttributeSticker):
|
||||
return media.document
|
||||
elif t is MessageMediaWebPage:
|
||||
if media.webpage.type == "photo":
|
||||
return media.webpage.photo
|
||||
return null
|
||||
|
||||
async def downloadProfilePicture(self, entity):
|
||||
media = entity.photo
|
||||
if not media or isinstance(media, ChatPhotoEmpty):
|
||||
return null
|
||||
mid = str(media.photo_id)
|
||||
if mid not in self.files:
|
||||
dl = await self.client.download_profile_photo(entity, bytes)
|
||||
self.files[mid] = (str(len(self.files)), dl, "image/jpg")
|
||||
return self.files[mid][0]
|
||||
|
||||
async def encodeAuthor(self, msg):
|
||||
obj = dict()
|
||||
|
||||
uid, name, picture, adminTitle = await self.getAuthor(msg)
|
||||
|
||||
obj.id = uid
|
||||
obj.name = name
|
||||
if picture:
|
||||
obj.picture = {"file": picture}
|
||||
if adminTitle:
|
||||
obj.adminTitle = adminTitle
|
||||
|
||||
return obj
|
||||
|
||||
async def getAuthor(self, msg, full=true):
|
||||
uid = null
|
||||
name = null
|
||||
picture = null
|
||||
adminTitle = null
|
||||
|
||||
chat = msg.peer_id
|
||||
peer = msg.from_id or chat
|
||||
fwd = msg.fwd_from
|
||||
if fwd:
|
||||
peer = fwd.from_id
|
||||
name = fwd.post_author or fwd.from_name
|
||||
|
||||
t = type(peer)
|
||||
if t is int:
|
||||
uid = peer
|
||||
elif t is PeerUser:
|
||||
uid = peer.user_id
|
||||
elif t is PeerChannel:
|
||||
uid = peer.channel_id
|
||||
elif t is PeerChat:
|
||||
uid = peer.chat_id
|
||||
elif t is PeerBlocked:
|
||||
uid = peer.peer_id
|
||||
elif not peer:
|
||||
uid = int(hashlib.shake_256(name.encode("utf-8")).hexdigest(6), 16)
|
||||
|
||||
if not name:
|
||||
entity = null
|
||||
try:
|
||||
entity = await self.client.get_entity(peer)
|
||||
except Exception:
|
||||
entity = await msg.get_chat()
|
||||
|
||||
if isinstance(entity, User) and entity.deleted:
|
||||
name = "Deleted Account"
|
||||
else:
|
||||
name = utils.get_display_name(entity)
|
||||
|
||||
if full:
|
||||
picture = await self.downloadProfilePicture(entity)
|
||||
|
||||
if isinstance(chat, (PeerChannel, PeerChat)):
|
||||
admins = await self.client.get_participants(
|
||||
chat, filter=ChannelParticipantsAdmins
|
||||
)
|
||||
for admin in admins:
|
||||
participant = admin.participant
|
||||
if participant.user_id == uid:
|
||||
try:
|
||||
adminTitle = participant.rank
|
||||
except AttributeError:
|
||||
adminTitle = null
|
||||
if not adminTitle:
|
||||
if isinstance(participant, ChannelParticipantCreator):
|
||||
adminTitle = "owner"
|
||||
else:
|
||||
adminTitle = "admin"
|
||||
break
|
||||
|
||||
return uid, name, picture, adminTitle
|
||||
|
||||
async def encodeReply(self, reply):
|
||||
obj = dict()
|
||||
|
||||
text = reply.message
|
||||
if text:
|
||||
obj.text = text
|
||||
else:
|
||||
media = reply.media
|
||||
if media:
|
||||
t = type(media)
|
||||
if t is MessageMediaPhoto:
|
||||
obj.text = "📷 Photo"
|
||||
else:
|
||||
obj.text = "💾 File"
|
||||
|
||||
name = (await self.getAuthor(reply, full=false))[1]
|
||||
|
||||
obj.author = name
|
||||
|
||||
media = reply.media
|
||||
if media:
|
||||
file = await self.downloadMedia(media, -1)
|
||||
if file:
|
||||
obj.thumbnail = {"file": file}
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
async def update(modules, message, url=MODULE_PATH):
|
||||
loader = next(filter(lambda x: "LoaderMod" == x.__class__.__name__, modules))
|
||||
try:
|
||||
if await loader.download_and_install(url, message):
|
||||
loader._db.set(
|
||||
__name__,
|
||||
"loaded_modules",
|
||||
list(set(loader._db.get(__name__, "loaded_modules", [])).union([url])),
|
||||
)
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
except Exception:
|
||||
return false
|
||||
155
GeekTG/FTG-Modules/range.py
Normal file
155
GeekTG/FTG-Modules/range.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def register(cb):
|
||||
cb(RangeMod())
|
||||
|
||||
|
||||
@loader.tds
|
||||
class RangeMod(loader.Module):
|
||||
"""Provides numbers as in Python range with delay"""
|
||||
|
||||
strings = {
|
||||
"name": "Python range",
|
||||
"no_args": "<b>Not enough args (minimum {})</b>",
|
||||
"delay_num": "<b>Delay must be a number</b>",
|
||||
"args_int": "<b>All range args must be integers</b>",
|
||||
"many_args": "<b>There must be no more than {} arguments</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"msg_format",
|
||||
"{0}",
|
||||
"Format of each message. {0} replaces current number.",
|
||||
"default_delay",
|
||||
1.0,
|
||||
"Delay in all commands by default",
|
||||
)
|
||||
self.name = self.strings["name"]
|
||||
|
||||
def config_complete(self):
|
||||
self.name = self.strings["name"]
|
||||
|
||||
async def _do_range(self, range_args, delay, message):
|
||||
"""for internal usage; do range itself"""
|
||||
await message.delete()
|
||||
for now in range(*range_args):
|
||||
await message.respond(self.config["msg_format"].format(now))
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
async def _get_args(self, message, minn, maxn):
|
||||
args = utils.get_args(message)
|
||||
if len(args) < minn:
|
||||
logger.warning(
|
||||
f'Minimum {minn} {"args" if minn != 1 else "arg"}, {len(args)} provided'
|
||||
)
|
||||
await utils.answer(message, self.strings["no_args"].format(minn))
|
||||
return None
|
||||
elif len(args) > maxn:
|
||||
logger.warning(
|
||||
f'Maximum {maxn} {"args" if maxn != 1 else "arg"}, {len(args)} provided'
|
||||
)
|
||||
await utils.answer(message, self.strings["many_args"].format(maxn))
|
||||
return None
|
||||
return args
|
||||
|
||||
async def _check_range_args(self, range_args, message):
|
||||
"""for internal usage; check if range args are int"""
|
||||
try:
|
||||
range_args = [int(x) for x in range_args]
|
||||
return range_args
|
||||
except ValueError:
|
||||
logger.warning(
|
||||
f"Impossible to convert all range args to int ({range_args})"
|
||||
)
|
||||
await utils.answer(message, self.strings["args_int"])
|
||||
return None
|
||||
|
||||
async def rangecmd(self, message):
|
||||
"""Iterates over the given range and returns each number in separate message.\nUsage: .range <python_range_args>"""
|
||||
args = await self._get_args(message, 1, 3)
|
||||
if args is None:
|
||||
return # user done sth wrong
|
||||
|
||||
delay = self.config["default_delay"]
|
||||
range_args = await self._check_range_args(args, message)
|
||||
if range_args is None:
|
||||
return # user done sth wrong
|
||||
|
||||
await self._do_range(range_args, delay, message)
|
||||
|
||||
async def drangecmd(self, message):
|
||||
"""Iterates over the given range and returns each number in separate message.\nUsage: .drange <delay> <python_range_args>"""
|
||||
args = await self._get_args(message, 2, 4)
|
||||
if args is None:
|
||||
return # user done sth wrong
|
||||
|
||||
try:
|
||||
delay = float(args[0])
|
||||
except ValueError:
|
||||
logger.warning(f"Impossible to convert delay to float ({args[0]})")
|
||||
await utils.answer(message, self.strings["delay_num"])
|
||||
return
|
||||
|
||||
range_args = await self._check_range_args(args[1:], message)
|
||||
if range_args is None:
|
||||
return
|
||||
|
||||
await self._do_range(range_args, delay, message)
|
||||
|
||||
async def countcmd(self, message):
|
||||
"""Count from 1 to N.\nUsage: .count <delay> <N> or .count <N>"""
|
||||
args = await self._get_args(message, 1, 2)
|
||||
if args is None:
|
||||
return
|
||||
|
||||
if len(args) == 1:
|
||||
delay = self.config["default_delay"]
|
||||
range_args = (1, args[0], 1)
|
||||
elif len(args) == 2:
|
||||
try:
|
||||
delay = float(args[0])
|
||||
except ValueError:
|
||||
logger.warning(f"Impossible to convert delay to float ({args[0]})")
|
||||
await utils.answer(message, self.strings["delay_num"])
|
||||
return
|
||||
range_args = (1, args[1], 1)
|
||||
|
||||
range_args = await self._check_range_args(range_args, message)
|
||||
if range_args is None:
|
||||
return
|
||||
range_args[1] += 1 # so last number we print will be N itself
|
||||
|
||||
await self._do_range(range_args, delay, message)
|
||||
|
||||
async def rcountcmd(self, message):
|
||||
"""Count from N to 1.\nUsage: .rcount <delay> <N> or .rcount <N>"""
|
||||
args = await self._get_args(message, 1, 2)
|
||||
if args is None:
|
||||
return
|
||||
|
||||
if len(args) == 1:
|
||||
delay = self.config["default_delay"]
|
||||
range_args = (args[0], 0, -1)
|
||||
elif len(args) == 2:
|
||||
try:
|
||||
delay = float(args[0])
|
||||
except ValueError:
|
||||
logger.warning(f"Impossible to convert delay to float ({args[0]})")
|
||||
await utils.answer(message, self.strings["delay_num"])
|
||||
return
|
||||
range_args = (args[1], 0, -1)
|
||||
|
||||
range_args = await self._check_range_args(range_args, message)
|
||||
if range_args is None:
|
||||
return
|
||||
|
||||
await self._do_range(range_args, delay, message)
|
||||
63
GeekTG/FTG-Modules/recent_actions.py
Normal file
63
GeekTG/FTG-Modules/recent_actions.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import telethon
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class RecentActionsMod(loader.Module):
|
||||
"""Reads recent actions"""
|
||||
|
||||
strings = {
|
||||
"name": "Recent Actions",
|
||||
"reply_start": "<b>Reply to a message to specify where to start</b>",
|
||||
"invalid_chat": "<b>This isn't a supergroup or channel</b>",
|
||||
"needs_admin": "<b>Admin rights are required to read deleted messages</b>",
|
||||
"recovered": "Deleted message {} recovered. Originally sent at {} by {}, deleted at {} by {}",
|
||||
}
|
||||
|
||||
@loader.group_admin
|
||||
@loader.ratelimit
|
||||
async def recoverdeletedcmd(self, message):
|
||||
"""Restores deleted messages sent after replied message (optionally specify how many to recover)"""
|
||||
msgs = message.client.iter_admin_log(message.to_id, delete=True)
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("reply_start", message))
|
||||
return
|
||||
if not isinstance(message.to_id, telethon.tl.types.PeerChannel):
|
||||
await utils.answer(message, self.strings("invalid_chat", message))
|
||||
return
|
||||
target = (await message.get_reply_message()).date
|
||||
ret = []
|
||||
try:
|
||||
async for msg in msgs:
|
||||
if msg.original.date < target:
|
||||
break
|
||||
if msg.original.action.message.date < target:
|
||||
continue
|
||||
ret += [msg]
|
||||
except telethon.errors.rpcerrorlist.ChatAdminRequiredError:
|
||||
await utils.answer(message, self.strings("needs_admin", message))
|
||||
args = utils.get_args(message)
|
||||
if len(args) > 0:
|
||||
try:
|
||||
count = int(args[0])
|
||||
ret = ret[-count:]
|
||||
except ValueError:
|
||||
pass
|
||||
for msg in reversed(ret):
|
||||
orig = msg.original.action.message
|
||||
deldate = msg.original.date.isoformat()
|
||||
origdate = orig.date.isoformat()
|
||||
await message.respond(
|
||||
self.strings("recovered", message).format(
|
||||
msg.id, origdate, orig.sender_id, deldate, msg.user_id
|
||||
)
|
||||
)
|
||||
if isinstance(orig, telethon.tl.types.MessageService):
|
||||
await message.respond(
|
||||
"<b>" + utils.escape_html(orig.stringify()) + "</b>"
|
||||
)
|
||||
else:
|
||||
await message.respond(orig)
|
||||
72
GeekTG/FTG-Modules/requirements.txt
Normal file
72
GeekTG/FTG-Modules/requirements.txt
Normal file
@@ -0,0 +1,72 @@
|
||||
aiohttp
|
||||
aiohttp-jinja2
|
||||
appdirs
|
||||
async-timeout
|
||||
attrs
|
||||
Babel
|
||||
beautifulsoup4
|
||||
cairocffi
|
||||
CairoSVG
|
||||
certifi
|
||||
cffi
|
||||
chardet
|
||||
click
|
||||
coffeehouse
|
||||
coloredlogs
|
||||
cssselect2
|
||||
Cython
|
||||
defusedxml
|
||||
distlib
|
||||
filelock
|
||||
gitdb
|
||||
GitPython
|
||||
googletrans
|
||||
gTTS
|
||||
h11
|
||||
h2
|
||||
hachoir
|
||||
heroku3
|
||||
hpack
|
||||
hstspreload
|
||||
httpcore
|
||||
httpx
|
||||
humanfriendly
|
||||
hyperframe
|
||||
idna
|
||||
Jinja2
|
||||
lxml
|
||||
lyricsgenius
|
||||
MarkupSafe
|
||||
mautrix
|
||||
meval
|
||||
multidict
|
||||
numpy
|
||||
Pillow
|
||||
pyaes
|
||||
pyasn1
|
||||
pycparser
|
||||
pydub
|
||||
Pygments
|
||||
python-dateutil
|
||||
pythondialog
|
||||
pytz
|
||||
requests
|
||||
rfc3986
|
||||
rsa
|
||||
search-engine-parser
|
||||
ShazamAPI
|
||||
six
|
||||
smmap
|
||||
sniffio
|
||||
soupsieve
|
||||
speedtest-cli
|
||||
Telethon-Mod
|
||||
tinycss2
|
||||
tk
|
||||
typing-extensions
|
||||
urllib3
|
||||
virtualenv
|
||||
Wand
|
||||
webencodings
|
||||
yarl
|
||||
youtube-dl
|
||||
526
GeekTG/FTG-Modules/rpmod.py
Normal file
526
GeekTG/FTG-Modules/rpmod.py
Normal file
@@ -0,0 +1,526 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @trololo_1
|
||||
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import emoji
|
||||
except:
|
||||
mod_inst = subprocess.Popen("pip install emoji", shell=True)
|
||||
mod_inst.wait()
|
||||
import emoji
|
||||
|
||||
import string
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class RPMod(loader.Module):
|
||||
"""Модуль RPMod + дополнение после команды.+реплика.(указывать реплику на второй строке)"""
|
||||
|
||||
strings = {"name": "RPMod"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
if not self.db.get("RPMod", "exlist", False):
|
||||
exlist = []
|
||||
self.db.set("RPMod", "exlist", exlist)
|
||||
if not self.db.get("RPMod", "status", False):
|
||||
self.db.get("RPMod", "status", 1)
|
||||
if not self.db.get("RPMod", "rprezjim", False):
|
||||
self.db.set("RPMod", "rprezjim", 1)
|
||||
if not self.db.get("RPMod", "rpnick", False):
|
||||
me = await client.get_me()
|
||||
self.db.set("RPMod", "rpnick", me.first_name)
|
||||
if not self.db.get("RPMod", "rpcomands", False):
|
||||
comands = {
|
||||
"чмок": "чмокнул",
|
||||
"кусь": "кусьнул",
|
||||
"пиц": "радостные звуки питсы",
|
||||
"поцеловать": "поцеловал",
|
||||
"ррр": "злые звуки питсы",
|
||||
"выебать": "выебал",
|
||||
"трахнуть": "трахнул",
|
||||
"выпороть": "выпорол",
|
||||
"шлепнуть": "шлепнул",
|
||||
"отлизать": "отлизал у",
|
||||
"прижать": "прижал",
|
||||
"погладить": "погладил",
|
||||
"да.": "пизда",
|
||||
"где.": "в пизде",
|
||||
"нет.": "пидора ответ",
|
||||
"бывает.": "ну это пиздец конечно на самом деле",
|
||||
"мрр.": "мурчание питсы",
|
||||
"ррррр.": "злая питса",
|
||||
"обнять": "обнял",
|
||||
}
|
||||
self.db.set("RPMod", "rpcomands", comands)
|
||||
if not self.db.get("RPMod", "rpemoji", False):
|
||||
emojiComands = {"лизь": "👅"}
|
||||
self.db.set("RPMod", "rpemoji", emojiComands)
|
||||
|
||||
async def dobrpcmd(self, message):
|
||||
"""Используй: .dobrp (команда) / (действие) / (эмодзи) чтобы добавить команду. Можно и без эмодзи(но и второго
|
||||
разделителя). Используй только одно слово в качестве команды."""
|
||||
await message.edit("<code>Команда добавляется...</coode>")
|
||||
args = utils.get_args_raw(message)
|
||||
dict_rp = self.db.get("RPMod", "rpcomands")
|
||||
dict_rp_copy = dict_rp.copy()
|
||||
try:
|
||||
key_rp = str(args.split("/")[0]).strip()
|
||||
value_rp = str(args.split("/", maxsplit=2)[1]).strip()
|
||||
lenght_args = []
|
||||
for i in args.split("/"):
|
||||
lenght_args.append(i)
|
||||
count_emoji = 0
|
||||
|
||||
if len(lenght_args) >= 3:
|
||||
emoji_rp = str(args.split("/", maxsplit=2)[2]).strip()
|
||||
dict_emoji_rp = self.db.get("RPMod", "rpemoji")
|
||||
dict_emoji_rp_copy = dict_emoji_rp.copy()
|
||||
r = emoji_rp
|
||||
lst = []
|
||||
count_emoji = 1
|
||||
for x in r:
|
||||
if x in emoji.UNICODE_EMOJI["en"].keys():
|
||||
lst.append(x)
|
||||
if (
|
||||
x.isalpha()
|
||||
or x.isspace()
|
||||
or x.isdigit()
|
||||
or x in string.punctuation
|
||||
):
|
||||
await message.edit(
|
||||
"<b>Были введены не только эмодзи(пробел тоже символ). </b>"
|
||||
)
|
||||
return
|
||||
if len(lst) > 3:
|
||||
await message.edit("<b>Было введено более 3 эмодзи. </b>")
|
||||
return
|
||||
if not emoji_rp or not emoji_rp.strip():
|
||||
await message.edit(
|
||||
"<b>Разделитель для эмодзи есть, а их нет? хм.</b>"
|
||||
)
|
||||
return
|
||||
if len(lst) == 0:
|
||||
await message.edit(
|
||||
"<b>В 3 секции были введены не эмодзи. Если были введены эмодзи, но всё равно выходит ошибка, обратись к: </b>@trololo_1"
|
||||
)
|
||||
return
|
||||
|
||||
key_len = [len(x) for x in key_rp.split()]
|
||||
|
||||
if len(dict_rp) >= 70:
|
||||
await message.edit("<b>Достигнут лимит рп команд.</b>")
|
||||
else:
|
||||
if not key_rp or not key_rp.strip():
|
||||
await message.edit("<b>Вы не ввели название рп команды.</b>")
|
||||
else:
|
||||
if not value_rp or not value_rp.strip():
|
||||
await message.edit(
|
||||
"<b>Вы не ввели действие для рп команды.</b>"
|
||||
)
|
||||
else:
|
||||
if int(len(key_len)) > 1:
|
||||
await message.edit(
|
||||
"<b>В качестве рп команды было введено больше одного слова.</b>"
|
||||
)
|
||||
else:
|
||||
if key_rp == "all":
|
||||
await message.edit(
|
||||
"<b>Использовать '<code>all</code>' в качестве названия команды запрещено!</b>"
|
||||
)
|
||||
else:
|
||||
if count_emoji == 1:
|
||||
dict_emoji_rp_copy[key_rp] = emoji_rp
|
||||
dict_rp_copy[key_rp] = value_rp
|
||||
self.db.set("RPMod", "rpcomands", dict_rp_copy)
|
||||
self.db.set("RPMod", "rpemoji", dict_emoji_rp_copy)
|
||||
await message.edit(
|
||||
f"<b>Команда '<code>{key_rp}</code>' успешно добавлена с эмодзи '{emoji_rp}'!</b>"
|
||||
)
|
||||
else:
|
||||
dict_rp_copy[key_rp] = value_rp
|
||||
self.db.set("RPMod", "rpcomands", dict_rp_copy)
|
||||
await message.edit(
|
||||
f"<b>Команда '<code>{key_rp}</code>' успешно добавлена!</b>"
|
||||
)
|
||||
except:
|
||||
await message.edit(
|
||||
"<b>Вы не ввели разделитель /, либо вовсе ничего не ввели.</b>"
|
||||
)
|
||||
|
||||
async def delrpcmd(self, message):
|
||||
"""Используй: .delrp (команда) чтобы удалить команду.\n Используй: .delrp all чтобы удалить все команды."""
|
||||
await message.edit("Команда удаляется..")
|
||||
args = utils.get_args_raw(message)
|
||||
dict_rp = self.db.get("RPMod", "rpcomands")
|
||||
dict_emoji_rp = self.db.get("RPMod", "rpemoji")
|
||||
dict_emoji_rp_copy = dict_emoji_rp.copy()
|
||||
dict_rp_copy = dict_rp.copy()
|
||||
|
||||
key_rp = str(args)
|
||||
count = 0
|
||||
if key_rp == "all":
|
||||
dict_rp_copy.clear()
|
||||
dict_emoji_rp_copy.clear()
|
||||
self.db.set("RPMod", "rpcomands", dict_rp_copy)
|
||||
self.db.set("RPMod", "rpemoji", dict_emoji_rp_copy)
|
||||
await message.edit("<b>Список рп команд очищен.</b>")
|
||||
return
|
||||
if not key_rp or not key_rp.strip():
|
||||
await message.edit("<b>Вы не ввели команду.</b>")
|
||||
else:
|
||||
try:
|
||||
for i in dict_emoji_rp_copy:
|
||||
if i == key_rp:
|
||||
count = 1
|
||||
break
|
||||
if count == 1:
|
||||
dict_rp_copy.pop(key_rp)
|
||||
dict_emoji_rp_copy.pop(key_rp)
|
||||
self.db.set("RPMod", "rpcomands", dict_rp_copy)
|
||||
self.db.set("RPMod", "rpemoji", dict_emoji_rp_copy)
|
||||
else:
|
||||
dict_rp_copy.pop(key_rp)
|
||||
self.db.set("RPMod", "rpcomands", dict_rp_copy)
|
||||
await message.edit(
|
||||
f"<b>Команда '<code>{key_rp}</code>' успешно удалена!</b>"
|
||||
)
|
||||
except KeyError:
|
||||
await message.edit("<b>Команда не найдена.</b>")
|
||||
|
||||
async def rpmodcmd(self, message):
|
||||
"""Используй: .rpmod чтобы включить/выключить RP режим.\nИспользуй: .rpmod toggle чтобы сменить режим на отправку или изменение смс."""
|
||||
status = self.db.get("RPMod", "status")
|
||||
rezjim = self.db.get("RPMod", "rprezjim")
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
if status == 1:
|
||||
self.db.set("RPMod", "status", 2)
|
||||
await message.edit("<b>RP Режим <code>выключен</code></b>")
|
||||
else:
|
||||
self.db.set("RPMod", "status", 1)
|
||||
await message.edit("<b>RP Режим <code>включен</code></b>")
|
||||
elif args.strip() == "toggle":
|
||||
if rezjim == 1:
|
||||
self.db.set("RPMod", "rprezjim", 2)
|
||||
await message.edit(
|
||||
"<b>RP Режим изменён на <code>отправку смс.</code></b>"
|
||||
)
|
||||
else:
|
||||
self.db.set("RPMod", "rprezjim", 1)
|
||||
await message.edit(
|
||||
"<b>RP Режим изменён на <code>изменение смс.</code></b>"
|
||||
)
|
||||
else:
|
||||
await message.edit("Что то не так.. ")
|
||||
|
||||
async def rplistcmd(self, message):
|
||||
"""Используй: .rplist чтобы посмотреть список рп команд."""
|
||||
com = self.db.get("RPMod", "rpcomands")
|
||||
emojies = self.db.get("RPMod", "rpemoji")
|
||||
l = len(com)
|
||||
|
||||
listComands = f"У вас рп команд: <b>{l}</b> из <b>70</b>. "
|
||||
if len(com) == 0:
|
||||
await message.edit("<b>Увы, у вас нету рп команд. :(</b>")
|
||||
return
|
||||
for i in com:
|
||||
if i in emojies.keys():
|
||||
listComands += f"\n• <b><code>{i}</code> - {com[i]} |</b> {emojies[i]}"
|
||||
else:
|
||||
listComands += f"\n• <b><code>{i}</code> - {com[i]}</b>"
|
||||
await message.edit(listComands)
|
||||
|
||||
async def rpnickcmd(self, message):
|
||||
"Используй: .rpnick (ник) чтобы сменить свой ник. Если без аргументов, то вернётся ник из тг."
|
||||
r = utils.get_args_raw(message).strip()
|
||||
if not r:
|
||||
me = await message.client.get_me()
|
||||
self.db.set("RPMod", "rpnick", me.first_name)
|
||||
await message.edit(f"<b>Ник изменён на {me.first_name}</b>")
|
||||
return
|
||||
lst = []
|
||||
nick = ""
|
||||
for x in r:
|
||||
if x in emoji.UNICODE_EMOJI["en"].keys():
|
||||
lst.append(x)
|
||||
if x not in emoji.UNICODE_EMOJI["en"].keys():
|
||||
nick += x
|
||||
if len(lst) > 3:
|
||||
await message.edit(f"<b>Ник '{r}' содержит более трёх эмодзи.</b>")
|
||||
else:
|
||||
if len(lst) + len(nick) >= 45:
|
||||
await message.edit(
|
||||
"<b>Ник превышает лимит в 45 символов(возможно эмодзи имеют длину более 1 символа).</b>"
|
||||
)
|
||||
else:
|
||||
self.db.set("RPMod", "rpnick", r)
|
||||
await message.edit(f"<b>Ник изменён на {r}</b>")
|
||||
|
||||
async def rpbackcmd(self, message):
|
||||
"Используй: .rpback чтобы выгрузить список своих рп команд.\nИспользуй .rpback / (список чьих то команд) / (список чьих то эмодзи) чтобы добавить себе список команд. можно без эмодзи, но первый разделитель обязателен."
|
||||
args = utils.get_args_raw(message).strip()
|
||||
comand = self.db.get("RPMod", "rpcomands")
|
||||
emojies = self.db.get("RPMod", "rpemoji")
|
||||
if not args:
|
||||
if len(comand) == 0:
|
||||
await message.edit("<b>У вас нет рп команд.</b>")
|
||||
elif len(emojies) == 0:
|
||||
await message.edit(f"<code>.rpback / {comand} </code>")
|
||||
else:
|
||||
await message.edit(f"<code>.rpback / {comand} / {emojies}</code>")
|
||||
if args:
|
||||
try:
|
||||
comands = str(args.split("/")[1]).strip()
|
||||
|
||||
lenght_args = []
|
||||
for i in args.split("/"):
|
||||
lenght_args.append(i)
|
||||
count_emoji = 0
|
||||
|
||||
if len(lenght_args) >= 3:
|
||||
emoji_rp = str(args.split("/")[2]).strip()
|
||||
count_emoji = 1
|
||||
emj = eval(emoji_rp)
|
||||
if not dict == type(emj):
|
||||
await message.edit(
|
||||
"<b>Синтаксис секции эмодзи не является корректным(словарём в питоне).</b>"
|
||||
)
|
||||
else:
|
||||
if len(emj) == 0:
|
||||
await message.edit("<b>Словарь эмодзи пуст.</b>")
|
||||
return
|
||||
for x in emj.values():
|
||||
lst = []
|
||||
if x in emoji.UNICODE_EMOJI["en"].keys():
|
||||
lst.append(x)
|
||||
if not x or not x.strip():
|
||||
await message.edit(
|
||||
"<b>Пустое значение в словаре для эмодзи? Да ты гений.</b>"
|
||||
)
|
||||
return
|
||||
if (
|
||||
x.isalpha()
|
||||
or x.isspace()
|
||||
or x.isdigit()
|
||||
or x in string.punctuation
|
||||
):
|
||||
await message.edit(
|
||||
"<b>Были введены не только эмодзи в словаре для эмодзи(пробел тоже символ). </b>"
|
||||
)
|
||||
return
|
||||
if len(lst) > 3:
|
||||
await message.edit(
|
||||
"<b>Было введено более 3 эмодзи в словаре для эмодзи. </b>"
|
||||
)
|
||||
return
|
||||
|
||||
for x in emj:
|
||||
key_len = [len(l) for l in x.split()]
|
||||
if int(len(key_len)) > 1:
|
||||
await message.edit(
|
||||
"<b>В качестве ключа было введено больше одного слова в словаре эмодзи.</b>"
|
||||
)
|
||||
return
|
||||
if not x or not x.strip():
|
||||
await message.edit(
|
||||
"<b>Пустой ключ в словаре для эмодзи? Да ты умничка.</b>"
|
||||
)
|
||||
return
|
||||
com = eval(comands)
|
||||
if dict == type(com):
|
||||
if len(com) == 0:
|
||||
await message.edit("<b>Словарь команд пуст.</b>")
|
||||
return
|
||||
for x in com:
|
||||
key_len_cmd = [len(l) for l in x.split()]
|
||||
if int(len(key_len_cmd)) > 1:
|
||||
await message.edit(
|
||||
"<b>В качестве ключа было введено больше одного слова в словаре команд.</b>"
|
||||
)
|
||||
return
|
||||
if not x or not x.strip():
|
||||
await message.edit(
|
||||
"<b>Пустой ключ в словаре для команд? Мой хороший, так дела не делаются.</b>"
|
||||
)
|
||||
return
|
||||
for x in com.values():
|
||||
if not x or not x.strip():
|
||||
await message.edit(
|
||||
"<b>Пустое значение в словаре для команд? Не сегодня, мой золотой.</b>"
|
||||
)
|
||||
return
|
||||
if count_emoji == 1:
|
||||
comand_copy = comand.copy()
|
||||
emojies_copy = emojies.copy()
|
||||
merge_emj = {**emojies_copy, **emj}
|
||||
merge_com = {**comand_copy, **com}
|
||||
self.db.set("RPMod", "rpcomands", merge_com)
|
||||
self.db.set("RPMod", "rpemoji", merge_emj)
|
||||
await message.edit(
|
||||
"<b>Успешное обновлени словаря команд и эмодзи! Вы можете просмотреть их благодаря команде '<code>.rplist</code>'.</b>"
|
||||
)
|
||||
else:
|
||||
comand_copy = comand.copy()
|
||||
merge_com = {**comand_copy, **com}
|
||||
self.db.set("RPMod", "rpcomands", merge_com)
|
||||
await message.edit(
|
||||
"<b>Успешное обновлени словаря команд! Вы можете просмотреть его благодаря команде '<code>.rplist</code>'.</b>"
|
||||
)
|
||||
else:
|
||||
await message.edit(
|
||||
"<b>Синтаксис секции команд не является корректным(словарём в питоне).</b>"
|
||||
)
|
||||
except:
|
||||
await message.edit(
|
||||
"<b>Что то не так с разделителями /.\nЛибо не корректный словарь.(либо вообще пусто)</b>"
|
||||
)
|
||||
|
||||
async def rpblockcmd(self, message):
|
||||
"""Используй: .rpblock чтобы добавить/удалить исключение(использовать в нужном чате).\nИспользуй: .rpblock list чтобы просмотреть чаты в исключениях.\nИспользуй .rpblock (ид) чтобы удалить чат из исключений."""
|
||||
args = utils.get_args_raw(message)
|
||||
ex = self.db.get("RPMod", "exlist")
|
||||
ex_copy = ex.copy()
|
||||
if not args:
|
||||
a = await message.client.get_entity(message.to_id)
|
||||
if a.id in ex_copy:
|
||||
ex_copy.remove(a.id)
|
||||
self.db.set("RPMod", "exlist", ex_copy)
|
||||
try:
|
||||
name = a.title
|
||||
except:
|
||||
name = a.first_name
|
||||
await message.edit(
|
||||
f"<i>Чат <b><u>{name}</u></b>(<code>{a.id}</code>) удален из исключений.</i>"
|
||||
)
|
||||
else:
|
||||
ex_copy.append(a.id)
|
||||
self.db.set("RPMod", "exlist", ex_copy)
|
||||
try:
|
||||
name = a.title
|
||||
except:
|
||||
name = a.first_name
|
||||
await message.edit(
|
||||
f"<i>Чат <b><u>{name}</u></b>(<code>{a.id}</code>) добавлен в исключения.</i>"
|
||||
)
|
||||
elif args.isdigit():
|
||||
args = args.strip()
|
||||
args = int(args)
|
||||
if args in ex_copy:
|
||||
a = await message.client.get_entity(args)
|
||||
ex_copy.remove(args)
|
||||
self.db.set("RPMod", "exlist", ex_copy)
|
||||
try:
|
||||
name = a.title
|
||||
except:
|
||||
name = a.first_name
|
||||
await message.edit(
|
||||
f"<i>Чат <b><u>{name}</u></b>(<code>{args}</code>) удален из исключений.</i>"
|
||||
)
|
||||
else:
|
||||
await message.edit("<b>Неверный ид.</b>")
|
||||
elif args == "list":
|
||||
ex_len = len(ex_copy)
|
||||
if ex_len == 0:
|
||||
await message.edit("<b>Список исключений пуст.</b>")
|
||||
return
|
||||
sms = f"<i> Чаты, которые есть в исключениях({ex_len}):</i>"
|
||||
for i in ex_copy:
|
||||
a = await message.client.get_entity(i)
|
||||
try:
|
||||
name = a.title
|
||||
except:
|
||||
name = a.first_name
|
||||
sms += f"\n• <b><u>{name}</u></b>(<code>{i}</code>)"
|
||||
await message.edit(sms)
|
||||
else:
|
||||
await message.edit("Что то пошло не так..")
|
||||
|
||||
async def watcher(self, message):
|
||||
try:
|
||||
status = self.db.get("RPMod", "status")
|
||||
comand = self.db.get("RPMod", "rpcomands")
|
||||
rezjim = self.db.get("RPMod", "rprezjim")
|
||||
emojies = self.db.get("RPMod", "rpemoji")
|
||||
nick = self.db.get("RPMod", "rpnick")
|
||||
ex = self.db.get("RPMod", "exlist")
|
||||
|
||||
args = message.text.lower()
|
||||
chat_rp = await message.client.get_entity(message.to_id)
|
||||
lines = []
|
||||
detail = []
|
||||
round = 1
|
||||
for line in args.splitlines():
|
||||
lines.append(line)
|
||||
for i in lines[0].split(" ", maxsplit=1):
|
||||
if round == 1:
|
||||
detail.append(i)
|
||||
else:
|
||||
detail.append(" " + i)
|
||||
round += 1
|
||||
if len(detail) < 2:
|
||||
detail.append(" ")
|
||||
reply = await message.get_reply_message()
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
me = await message.client.get_me()
|
||||
if status == 1:
|
||||
if chat_rp.id not in ex:
|
||||
if message.sender_id == me.id:
|
||||
for i in comand:
|
||||
if detail[0] == i:
|
||||
if detail[0] in emojies.keys():
|
||||
if len(lines) < 2:
|
||||
if rezjim == 1:
|
||||
await message.edit(
|
||||
f"{emojies[detail[0]]} | <a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
)
|
||||
else:
|
||||
await message.respond(
|
||||
f"{emojies[detail[0]]} | <a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
)
|
||||
else:
|
||||
if rezjim == 1:
|
||||
await message.edit(
|
||||
f"{emojies[detail[0]]} | <a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
+ f"\n<b>С репликой: </b>{lines[1]}"
|
||||
)
|
||||
else:
|
||||
await message.respond(
|
||||
f"{emojies[detail[0]]} | <a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
+ f"\n<b>С репликой: </b>{lines[1]}"
|
||||
)
|
||||
else:
|
||||
if len(lines) < 2:
|
||||
if rezjim == 1:
|
||||
await message.edit(
|
||||
f"<a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
)
|
||||
else:
|
||||
await message.respond(
|
||||
f"<a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
)
|
||||
else:
|
||||
if rezjim == 1:
|
||||
await message.edit(
|
||||
f"<a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
+ f"\n<b>С репликой: </b>{lines[1]}"
|
||||
)
|
||||
else:
|
||||
await message.respond(
|
||||
f"<a href=tg://user?id={me.id}>{nick}</a> {comand[detail[0]]} <a href=tg://user?id={user.id}>{user.first_name}</a>"
|
||||
+ detail[1]
|
||||
+ f"\n<b>С репликой: </b>{lines[1]}"
|
||||
)
|
||||
except:
|
||||
pass
|
||||
79
GeekTG/FTG-Modules/screenshot.py
Normal file
79
GeekTG/FTG-Modules/screenshot.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules
|
||||
|
||||
# requires: pygments
|
||||
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pygments
|
||||
from pygments.formatters import ImageFormatter
|
||||
from pygments.lexers import Python3Lexer
|
||||
from requests import get
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WebShotMod(loader.Module):
|
||||
"""Screenshot module"""
|
||||
|
||||
strings = {"name": "Screenshot"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
def __init__(self):
|
||||
self.name = self.strings["name"]
|
||||
|
||||
@loader.sudo
|
||||
async def webshotcmd(self, message):
|
||||
"""Reply to link"""
|
||||
reply = None
|
||||
link = utils.get_args_raw(message)
|
||||
if not link:
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.delete()
|
||||
return
|
||||
link = reply.raw_text
|
||||
await message.edit("<b>Screenshotting...</b>")
|
||||
url = "https://webshot.deam.io/{}/?width=1920&height=1080?type=png"
|
||||
file = get(url.format(link))
|
||||
if not file.ok:
|
||||
await message.edit("<b>Something went wrong...</b>")
|
||||
return
|
||||
file = io.BytesIO(file.content)
|
||||
file.name = "webScreenshot.png"
|
||||
file.seek(0)
|
||||
await message.client.send_file(message.to_id, file, reply_to=reply)
|
||||
await message.delete()
|
||||
|
||||
async def fileshotcmd(self, message):
|
||||
"""Reply to file"""
|
||||
await message.edit("<b>Screenshotting...</b>")
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await message.edit("<b>reply to file.py</b>")
|
||||
return
|
||||
media = reply.media
|
||||
if not media:
|
||||
await message.edit("<b>reply to file.py</b>")
|
||||
return
|
||||
file = await message.client.download_file(media)
|
||||
text = file.decode("utf-8")
|
||||
pygments.highlight(
|
||||
text,
|
||||
Python3Lexer(),
|
||||
ImageFormatter(font_name="DejaVu Sans Mono", line_numbers=True),
|
||||
"fileScreenshot.png",
|
||||
)
|
||||
await message.client.send_file(
|
||||
message.to_id, "fileScreenshot.png", force_document=True
|
||||
)
|
||||
os.remove("fileScreenshot.png")
|
||||
await message.delete()
|
||||
65
GeekTG/FTG-Modules/searcher.py
Normal file
65
GeekTG/FTG-Modules/searcher.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: Official Repo, @GovnoCodules
|
||||
|
||||
import io
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class SearchMod(loader.Module):
|
||||
"""Searcher module"""
|
||||
|
||||
strings = {
|
||||
"name": "Search",
|
||||
"search": "⚪⚪⚪\n⚪❓⚪\n⚪⚪⚪",
|
||||
"no_reply": "<b>Reply to image or sticker!</b>",
|
||||
"ya_result": '<a href="{}"><b>🔴⚪🔴|See</b>\n<b>⚪🔴⚪|Search</b>\n<b>⚪🔴⚪|Results</b></a>',
|
||||
"error": "<b>Something went wrong...</b>",
|
||||
}
|
||||
|
||||
@loader.owner
|
||||
async def yarscmd(self, message):
|
||||
""".yars <repy to image>"""
|
||||
reply = await message.get_reply_message()
|
||||
data = await check_media(message, reply)
|
||||
if not data:
|
||||
await utils.answer(message, self.strings("no_reply", message))
|
||||
return
|
||||
await utils.answer(message, self.strings("search", message))
|
||||
searchUrl = "https://yandex.ru/images/search"
|
||||
files = {"upfile": ("blob", data, "image/jpeg")}
|
||||
params = {
|
||||
"rpt": "imageview",
|
||||
"format": "json",
|
||||
"request": '{"blocks":[{"block":"b-page_type_search-by-image__link"}]}',
|
||||
}
|
||||
response = requests.post(searchUrl, params=params, files=files)
|
||||
if response.ok:
|
||||
query_string = json.loads(response.content)["blocks"][0]["params"]["url"]
|
||||
link = searchUrl + "?" + query_string
|
||||
text = self.strings("ya_result", message).format(link)
|
||||
await utils.answer(message, text)
|
||||
else:
|
||||
await utils.answer(message, self.strings("error", message))
|
||||
|
||||
|
||||
async def check_media(message, reply):
|
||||
if not reply or not reply.media:
|
||||
return None
|
||||
if reply.photo:
|
||||
data = reply.photo
|
||||
elif reply.document:
|
||||
if reply.gif or reply.video or reply.audio or reply.voice:
|
||||
return None
|
||||
data = reply.media.document
|
||||
else:
|
||||
return None
|
||||
if not data or data is None:
|
||||
return None
|
||||
data = await message.client.download_file(data, bytes)
|
||||
return io.BytesIO(data)
|
||||
497
GeekTG/FTG-Modules/squotes.py
Normal file
497
GeekTG/FTG-Modules/squotes.py
Normal file
@@ -0,0 +1,497 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# API/Module authors: @Fl1yd, @spypm
|
||||
# Thank for.... - no1!!, no1 helped us, we did everything ourselves
|
||||
# But we took one method of get messages from the Mishase's and Droox's modules
|
||||
|
||||
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
from time import gmtime
|
||||
from typing import List, Union
|
||||
|
||||
import requests
|
||||
import telethon
|
||||
from telethon.tl import types
|
||||
from telethon.tl.patched import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
def get_message_media(message: Message):
|
||||
data = None
|
||||
if message and message.media:
|
||||
data = (
|
||||
message.photo
|
||||
or message.sticker
|
||||
or message.video
|
||||
or message.video_note
|
||||
or message.gif
|
||||
or message.web_preview
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
def get_entities(entities: types.TypeMessageEntity):
|
||||
# coded by @droox
|
||||
r = []
|
||||
if entities:
|
||||
for entity in entities:
|
||||
entity = entity.to_dict()
|
||||
entity["type"] = entity.pop("_").replace("MessageEntity", "").lower()
|
||||
r.append(entity)
|
||||
return r
|
||||
|
||||
|
||||
def get_message_text(message: Message, reply: bool = False):
|
||||
return (
|
||||
"📷 Фото"
|
||||
if message.photo and reply
|
||||
else message.file.emoji + " Стикер"
|
||||
if message.sticker and reply
|
||||
else "📹 Видеосообщение"
|
||||
if message.video_note and reply
|
||||
else "📹 Видео"
|
||||
if message.video and reply
|
||||
else "🖼 GIF"
|
||||
if message.gif and reply
|
||||
else "📊 Опрос"
|
||||
if message.poll
|
||||
else "📍 Местоположение"
|
||||
if message.geo
|
||||
else "👤 Контакт"
|
||||
if message.contact
|
||||
else f"🎵 Голосовое сообщение: {strftime(message.voice.attributes[0].duration)}"
|
||||
if message.voice
|
||||
else f"🎧 Музыка: {strftime(message.audio.attributes[0].duration)} | {message.audio.attributes[0].performer} - {message.audio.attributes[0].title}"
|
||||
if message.audio
|
||||
else f"💾 Файл: {message.file.name}"
|
||||
if type(message.media) == types.MessageMediaDocument
|
||||
and not get_message_media(message)
|
||||
else f"{message.media.emoticon} Дайс: {message.media.value}"
|
||||
if type(message.media) == types.MessageMediaDice
|
||||
else f"Service message: {message.action.to_dict()['_']}"
|
||||
if type(message) == types.MessageService
|
||||
else ""
|
||||
)
|
||||
|
||||
|
||||
def strftime(time: Union[int, float]):
|
||||
t = gmtime(time)
|
||||
return (
|
||||
f"{t.tm_hour:02d}:" if t.tm_hour > 0 else ""
|
||||
) + f"{t.tm_min:02d}:{t.tm_sec:02d}"
|
||||
|
||||
|
||||
@loader.tds
|
||||
class ShitQuotesMod(loader.Module):
|
||||
"""
|
||||
Quotes by @sh1tchannel
|
||||
"""
|
||||
|
||||
strings = {
|
||||
"name": "SQuotes",
|
||||
"no_reply": "<b>[SQuotes]</b> Нет реплая",
|
||||
"processing": "<b>[SQuotes]</b> Обработка...",
|
||||
"api_processing": "<b>[SQuotes]</b> Ожидание API...",
|
||||
"api_error": "<b>[SQuotes]</b> Ошибка API",
|
||||
"loading_media": "<b>[SQuotes]</b> Отправка...",
|
||||
"no_args_or_reply": "<b>[SQuotes]</b> Нет аргументов или реплая",
|
||||
"args_error": "<b>[SQuotes]</b> При обработке аргументов произошла ошибка. Запрос был: <code>{}</code>",
|
||||
"too_many_messages": "<b>[SQuotes]</b> Слишком много сообщений. Максимум: <code>{}</code>",
|
||||
}
|
||||
|
||||
async def client_ready(self, client: telethon.TelegramClient, db: dict):
|
||||
self.client = client
|
||||
self.db = db
|
||||
self.api_endpoint = "https://quotes.fl1yd.su/generate"
|
||||
self.settings = self.get_settings()
|
||||
|
||||
async def qcmd(self, message: types.Message):
|
||||
"""
|
||||
Сокращение команды .sq
|
||||
"""
|
||||
|
||||
return await self.sqcmd(message)
|
||||
|
||||
async def sqcmd(self, message: Message):
|
||||
"""
|
||||
Использование:
|
||||
|
||||
• .sq <кол-во сообщений> + <реплай> + <!file - скидывает файлом (по желанию)> + <цвет (по желанию)>
|
||||
>>> .sq
|
||||
>>> .sq 2 #2d2d2d
|
||||
>>> .sq red
|
||||
>>> .sq !file
|
||||
"""
|
||||
|
||||
args: List[str] = utils.get_args(message)
|
||||
if not await message.get_reply_message():
|
||||
return await utils.answer(message, self.strings["no_reply"])
|
||||
|
||||
m = await utils.answer(message, self.strings["processing"])
|
||||
|
||||
isFile = "!file" in args
|
||||
[count] = [int(arg) for arg in args if arg.isdigit() and int(arg) > 0] or [1]
|
||||
[bg_color] = [arg for arg in args if arg != "!file" and not arg.isdigit()] or [
|
||||
self.settings["bg_color"]
|
||||
]
|
||||
|
||||
if count > self.settings["max_messages"]:
|
||||
return await utils.answer(
|
||||
m,
|
||||
self.strings["too_many_messages"].format(self.settings["max_messages"]),
|
||||
)
|
||||
|
||||
payload = {
|
||||
"messages": await self.quote_parse_messages(message, count),
|
||||
"quote_color": bg_color,
|
||||
"text_color": self.settings["text_color"],
|
||||
}
|
||||
|
||||
if self.settings["debug"]:
|
||||
file = open("SQuotesDebug.json", "w")
|
||||
json.dump(
|
||||
payload,
|
||||
file,
|
||||
indent=4,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
await message.respond(file=file.name)
|
||||
|
||||
await utils.answer(m, self.strings["api_processing"])
|
||||
|
||||
r = await self._api_request(payload)
|
||||
if r.status_code != 200:
|
||||
return await utils.answer(m, self.strings["api_error"])
|
||||
|
||||
quote = io.BytesIO(r.content)
|
||||
quote.name = "SQuote" + (".png" if isFile else ".webp")
|
||||
|
||||
await utils.answer(m, quote, force_document=isFile)
|
||||
return await m[-1].delete()
|
||||
|
||||
async def quote_parse_messages(self, message: Message, count: int):
|
||||
payloads = []
|
||||
messages = [
|
||||
msg
|
||||
async for msg in self.client.iter_messages(
|
||||
message.chat_id,
|
||||
count,
|
||||
reverse=True,
|
||||
add_offset=1,
|
||||
offset_id=(await message.get_reply_message()).id,
|
||||
)
|
||||
]
|
||||
|
||||
for message in messages:
|
||||
avatar = rank = reply_id = reply_name = reply_text = None
|
||||
entities = get_entities(message.entities)
|
||||
|
||||
if message.fwd_from:
|
||||
if message.fwd_from.from_id:
|
||||
if type(message.fwd_from.from_id) == types.PeerChannel:
|
||||
user_id = message.fwd_from.from_id.channel_id
|
||||
else:
|
||||
user_id = message.fwd_from.from_id.user_id
|
||||
try:
|
||||
user = await self.client.get_entity(user_id)
|
||||
except Exception:
|
||||
name, avatar = await self.get_profile_data(message.sender)
|
||||
return (
|
||||
"Вот блин, произошла ошибка. Возможно на этом канале тебя забанили, и невозможно получить информацию.",
|
||||
None,
|
||||
message.sender.id,
|
||||
name,
|
||||
avatar,
|
||||
"ошибка :(",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
name, avatar = await self.get_profile_data(user)
|
||||
user_id = user.id
|
||||
|
||||
elif name := message.fwd_from.from_name:
|
||||
user_id = message.chat_id
|
||||
else:
|
||||
if reply := await message.get_reply_message():
|
||||
reply_id = reply.sender.id
|
||||
reply_name = telethon.utils.get_display_name(reply.sender)
|
||||
reply_text = get_message_text(reply, True) + (
|
||||
". " + reply.raw_text
|
||||
if reply.raw_text and get_message_text(reply, True)
|
||||
else reply.raw_text or ""
|
||||
)
|
||||
|
||||
user = await self.client.get_entity(message.sender)
|
||||
name, avatar = await self.get_profile_data(user)
|
||||
user_id = user.id
|
||||
|
||||
if message.is_group and message.is_channel:
|
||||
admins = await self.client.get_participants(
|
||||
message.chat_id, filter=types.ChannelParticipantsAdmins
|
||||
)
|
||||
if user in admins:
|
||||
admin = admins[admins.index(user)].participant
|
||||
rank = admin.rank or (
|
||||
"creator"
|
||||
if type(admin) == types.ChannelParticipantCreator
|
||||
else "admin"
|
||||
)
|
||||
|
||||
media = await self.client.download_media(
|
||||
get_message_media(message), bytes, thumb=-1
|
||||
)
|
||||
media = base64.b64encode(media).decode() if media else None
|
||||
|
||||
via_bot = message.via_bot.username if message.via_bot else None
|
||||
text = (message.raw_text or "") + (
|
||||
(
|
||||
"\n\n" + get_message_text(message)
|
||||
if message.raw_text
|
||||
else get_message_text(message)
|
||||
)
|
||||
if get_message_text(message)
|
||||
else ""
|
||||
)
|
||||
|
||||
payloads.append(
|
||||
{
|
||||
"text": text,
|
||||
"media": media,
|
||||
"entities": entities,
|
||||
"author": {
|
||||
"id": user_id,
|
||||
"name": name,
|
||||
"avatar": avatar,
|
||||
"rank": rank or "",
|
||||
"via_bot": via_bot,
|
||||
},
|
||||
"reply": {"id": reply_id, "name": reply_name, "text": reply_text},
|
||||
}
|
||||
)
|
||||
|
||||
return payloads
|
||||
|
||||
async def fsqcmd(self, message: Message):
|
||||
"""
|
||||
Использование:
|
||||
|
||||
• .fsq <@ или ID> + <текст> - квота от юзера с @ или ID + указанный текст
|
||||
>>> .fsq @onetimeusername Вам пизда
|
||||
|
||||
• .fsq <реплай> + <текст> - квота от юзера с реплая + указанный текст
|
||||
>>> .fsq Я лох
|
||||
|
||||
• .fsq <@ или ID> + <текст> + -r + <@ или ID> + <текст> - квота с фейковым реплаем
|
||||
>>> .fsq @Fl1yd спасибо -r @onetimeusername Ты крутой
|
||||
|
||||
• .fsq <@ или ID> + <текст> + -r + <@ или ID> + <текст>; <аргументы> - квота с фейковыми мульти сообщениями
|
||||
>>> .fsq @onetimeusername Пацаны из @sh1tchannel, ждите награду за ахуенный ботнет; @guslslakkaakdkab чево; @Fl1yd НАШ БОТНЕТ ЛУЧШИЙ -r @guslslakkaakdkab чево
|
||||
"""
|
||||
|
||||
args: str = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not (args or reply):
|
||||
return await utils.answer(message, self.strings["no_args_or_reply"])
|
||||
|
||||
m = await utils.answer(message, self.strings["processing"])
|
||||
try:
|
||||
payload = await self.fakequote_parse_messages(args, reply)
|
||||
except (IndexError, ValueError):
|
||||
return await utils.answer(
|
||||
m, self.strings["args_error"].format(message.text)
|
||||
)
|
||||
|
||||
if len(payload) > self.settings["max_messages"]:
|
||||
return await utils.answer(
|
||||
m,
|
||||
self.strings["too_many_messages"].format(self.settings["max_messages"]),
|
||||
)
|
||||
|
||||
payload = {
|
||||
"messages": payload,
|
||||
"quote_color": self.settings["bg_color"],
|
||||
"text_color": self.settings["text_color"],
|
||||
}
|
||||
|
||||
if self.settings["debug"]:
|
||||
file = open("SQuotesDebug.json", "w")
|
||||
json.dump(
|
||||
payload,
|
||||
file,
|
||||
indent=4,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
await message.respond(file=file.name)
|
||||
|
||||
await utils.answer(m, self.strings["api_processing"])
|
||||
|
||||
r = await self._api_request(payload)
|
||||
if r.status_code != 200:
|
||||
return await utils.answer(m, self.strings["api_error"])
|
||||
|
||||
quote = io.BytesIO(r.content)
|
||||
quote.name = "SQuote.webp"
|
||||
|
||||
await utils.answer(m, quote)
|
||||
return await m[-1].delete()
|
||||
|
||||
async def fakequote_parse_messages(self, args: str, reply: Message):
|
||||
async def get_user(args: str):
|
||||
args_, text = args.split(), ""
|
||||
user = await self.client.get_entity(
|
||||
int(args_[0]) if args_[0].isdigit() else args_[0]
|
||||
)
|
||||
|
||||
if len(args_) < 2:
|
||||
user = await self.client.get_entity(
|
||||
int(args) if args.isdigit() else args
|
||||
)
|
||||
else:
|
||||
text = args.split(maxsplit=1)[1]
|
||||
return user, text
|
||||
|
||||
if reply or reply and args:
|
||||
user = reply.sender
|
||||
name, avatar = await self.get_profile_data(user)
|
||||
text = args or ""
|
||||
|
||||
else:
|
||||
messages = []
|
||||
for part in args.split("; "):
|
||||
user, text = await get_user(part)
|
||||
name, avatar = await self.get_profile_data(user)
|
||||
reply_id = reply_name = reply_text = None
|
||||
|
||||
if " -r " in part:
|
||||
user, text = await get_user("".join(part.split(" -r ")[0]))
|
||||
user2, text2 = await get_user("".join(part.split(" -r ")[1]))
|
||||
|
||||
name, avatar = await self.get_profile_data(user)
|
||||
name2, _ = await self.get_profile_data(user2)
|
||||
|
||||
reply_id = user2.id
|
||||
reply_name = name2
|
||||
reply_text = text2
|
||||
|
||||
messages.append(
|
||||
{
|
||||
"text": text,
|
||||
"media": None,
|
||||
"entities": None,
|
||||
"author": {
|
||||
"id": user.id,
|
||||
"name": name,
|
||||
"avatar": avatar,
|
||||
"rank": "",
|
||||
},
|
||||
"reply": {
|
||||
"id": reply_id,
|
||||
"name": reply_name,
|
||||
"text": reply_text,
|
||||
},
|
||||
}
|
||||
)
|
||||
return messages
|
||||
|
||||
return [
|
||||
{
|
||||
"text": text,
|
||||
"media": None,
|
||||
"entities": None,
|
||||
"author": {"id": user.id, "name": name, "avatar": avatar, "rank": ""},
|
||||
"reply": {"id": None, "name": None, "text": None},
|
||||
}
|
||||
]
|
||||
|
||||
async def get_profile_data(self, user: types.User):
|
||||
avatar = await self.client.download_profile_photo(user.id, bytes)
|
||||
return (
|
||||
telethon.utils.get_display_name(user),
|
||||
base64.b64encode(avatar).decode() if avatar else None,
|
||||
)
|
||||
|
||||
async def sqsetcmd(self, message: Message):
|
||||
"""
|
||||
Использование:
|
||||
|
||||
• .sqset <bg_color/text_color/debug> (<цвет для bg_color/text_color> <True/False для debug>)
|
||||
>>> .sqset bg_color #2d2d2d
|
||||
>>> .sqset debug true
|
||||
"""
|
||||
|
||||
args: List[str] = utils.get_args_raw(message).split(maxsplit=1)
|
||||
if not args:
|
||||
return await utils.answer(
|
||||
message,
|
||||
f"<b>[SQuotes]</b> Настройки:\n\n"
|
||||
f"Максимум сообщений (<code>max_messages</code>): {self.settings['max_messages']}\n"
|
||||
f"Цвет квоты (<code>bg_color</code>): {self.settings['bg_color']}\n"
|
||||
f"Цвет текста (<code>text_color</code>): {self.settings['text_color']}\n"
|
||||
f"Дебаг (<code>debug</code>): {self.settings['debug']}\n\n"
|
||||
f"Настроить можно с помощью <code>.sqset</code> <параметр> <значение> или <code>reset</code>",
|
||||
)
|
||||
|
||||
if args[0] == "reset":
|
||||
self.get_settings(True)
|
||||
return await utils.answer(
|
||||
message, "<b>[SQuotes - Settings]</b> Настойки квот были сброшены"
|
||||
)
|
||||
|
||||
if len(args) < 2:
|
||||
return await utils.answer(
|
||||
message, "<b>[SQuotes - Settings]</b> Недостаточно аргументов"
|
||||
)
|
||||
|
||||
mods = ["max_messages", "bg_color", "text_color", "debug"]
|
||||
if args[0] not in mods:
|
||||
return await utils.answer(
|
||||
message,
|
||||
f"<b>[SQuotes - Settings]</b> Такого парамерта нет, есть {', '.join(mods)}",
|
||||
)
|
||||
|
||||
elif args[0] == "debug":
|
||||
if args[1].lower() not in ["true", "false"]:
|
||||
return await utils.answer(
|
||||
message,
|
||||
"<b>[SQuotes - Settings]</b> Такого значения параметра нет, есть true/false",
|
||||
)
|
||||
self.settings[args[0]] = args[1].lower() == "true"
|
||||
|
||||
elif args[0] == "max_messages":
|
||||
if not args[1].isdigit():
|
||||
return await utils.answer(
|
||||
message, "<b>[SQuotes - Settings]</b> Это не число"
|
||||
)
|
||||
self.settings[args[0]] = int(args[1])
|
||||
|
||||
else:
|
||||
self.settings[args[0]] = args[1]
|
||||
|
||||
self.db.set("SQuotes", "settings", self.settings)
|
||||
return await utils.answer(
|
||||
message,
|
||||
f"<b>[SQuotes - Settings]</b> Значение параметра {args[0]} было выставлено на {args[1]}",
|
||||
)
|
||||
|
||||
def get_settings(self, force: bool = False):
|
||||
settings: dict = self.db.get("SQuotes", "settings", {})
|
||||
if not settings or force:
|
||||
settings.update(
|
||||
{
|
||||
"max_messages": 15,
|
||||
"bg_color": "#162330",
|
||||
"text_color": "#fff",
|
||||
"debug": False,
|
||||
}
|
||||
)
|
||||
self.db.set("SQuotes", "settings", settings)
|
||||
|
||||
return settings
|
||||
|
||||
async def _api_request(self, data: dict):
|
||||
return await utils.run_sync(requests.post, self.api_endpoint, json=data)
|
||||
460
GeekTG/FTG-Modules/stickers.py
Normal file
460
GeekTG/FTG-Modules/stickers.py
Normal file
@@ -0,0 +1,460 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# requires: lottie cairosvg Pillow
|
||||
|
||||
import asyncio
|
||||
import io
|
||||
import itertools
|
||||
import logging
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
from textwrap import wrap
|
||||
|
||||
import requests
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import lottie
|
||||
except OSError:
|
||||
logger.exception("Lottie not available")
|
||||
|
||||
warnings.simplefilter("error", Image.DecompressionBombWarning)
|
||||
|
||||
bytes_font = requests.get(
|
||||
"https://github.com/KeyZenD/l/blob/master/bold.ttf?raw=true"
|
||||
).content
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class StickersMod(loader.Module):
|
||||
"""Tasks with stickers"""
|
||||
|
||||
strings = {
|
||||
"name": "Stickers",
|
||||
"stickers_username_cfg_doc": "Bot to use to create stickers",
|
||||
"sticker_size_cfg_doc": "The size of one sticker",
|
||||
"default_sticker_emoji_cfg_doc": "The emoji to use for stickers by default",
|
||||
"what_pack": "<b>You must specify which pack you would like to add the sticker to</b>",
|
||||
"what_photo": "<b>Reply to a sticker or photo to add it to your sticker pack</b>",
|
||||
"not_animated_pack": "<b>Animated stickers can only be added to animated packs</b>",
|
||||
"internal_error": "<b>Something went wrong while adding the sticker</b>",
|
||||
"bad_emojis": "<b>The emoji(s) you gave are invalid</b>",
|
||||
"animated_pack": "<b>Non-animated stickers cannot be added to animated packs</b>",
|
||||
"new_pack": "<b>Create a sticker pack first</b>",
|
||||
"pack_full": "<b>That pack is full. Delete some stickers or try making a new pack</b>",
|
||||
"added": "<b>Sticker added to</b> <a href='{}'>pack</a><b>!</b>",
|
||||
"bad_animated_sticker": "<b>Reply to an animated sticker to convert to a GIF</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"STICKERS_USERNAME",
|
||||
"Stickers",
|
||||
lambda m: self.strings("stickers_username_cfg_doc", m),
|
||||
"STICKER_SIZE",
|
||||
(512, 512),
|
||||
lambda m: self.strings("sticker_size_cfg_doc", m),
|
||||
"DEFAULT_STICKER_EMOJI",
|
||||
"🤔",
|
||||
lambda m: self.strings("default_sticker_emoji_cfg_doc", m),
|
||||
)
|
||||
self._lock = asyncio.Lock()
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
async def kangcmd(self, message): # noqa: C901 # TODO: split this into helpers
|
||||
"""Use in reply or with an attached media:
|
||||
.kang <pack name> [emojis]
|
||||
If pack is not matched the most recently created will be used instead"""
|
||||
args = utils.get_args(message)
|
||||
if len(args) not in (1, 2):
|
||||
logger.debug("wrong args len(%s) or bad args(%s)", len(args), args)
|
||||
await utils.answer(message, self.strings("what_pack", message))
|
||||
return
|
||||
|
||||
if not message.is_reply:
|
||||
if message.sticker or message.photo:
|
||||
logger.debug("user sent photo/sticker directly not reply")
|
||||
sticker = message
|
||||
else:
|
||||
logger.debug("user didnt send any sticker/photo or reply")
|
||||
async for sticker in message.client.iter_messages(message.to_id, 10):
|
||||
if sticker.sticker or sticker.photo:
|
||||
break # Changes message into the right one
|
||||
else:
|
||||
sticker = await message.get_reply_message()
|
||||
if not (sticker.sticker or sticker.photo):
|
||||
await utils.answer(message, self.strings("what_photo", message))
|
||||
return
|
||||
logger.debug("user did send photo/sticker")
|
||||
if len(args) > 1:
|
||||
emojis = args[1]
|
||||
elif sticker.sticker:
|
||||
emojis = sticker.file.emoji
|
||||
else:
|
||||
emojis = None
|
||||
if not emojis:
|
||||
emojis = self.config["DEFAULT_STICKER_EMOJI"]
|
||||
logger.debug(emojis)
|
||||
animated = sticker.file.mime_type == "application/x-tgsticker"
|
||||
try:
|
||||
img = BytesIO()
|
||||
await sticker.download_media(file=img)
|
||||
img.seek(0)
|
||||
logger.debug(img)
|
||||
if animated:
|
||||
async with self._lock:
|
||||
conv = message.client.conversation(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
timeout=5,
|
||||
exclusive=True,
|
||||
)
|
||||
async with conv:
|
||||
first = await conv.send_message("/cancel")
|
||||
await conv.get_response()
|
||||
await conv.send_message("/addsticker")
|
||||
buttons = (await conv.get_response()).buttons
|
||||
if buttons is not None:
|
||||
logger.debug("there are buttons, good")
|
||||
button = click_buttons(buttons, args[0])
|
||||
await button.click()
|
||||
else:
|
||||
logger.warning("there's no buttons!")
|
||||
await message.client.send_message(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"], "/cancel"
|
||||
)
|
||||
await utils.answer(message, "Something went wrong")
|
||||
return
|
||||
# We have sent the pack we wish to modify.
|
||||
# Upload sticker
|
||||
r0 = await conv.get_response()
|
||||
if ".PSD" in r0.message:
|
||||
logger.error("bad response from stickerbot 0")
|
||||
logger.error(r0)
|
||||
await utils.answer(
|
||||
message, self.strings("not_animated_pack", message)
|
||||
)
|
||||
msgs = []
|
||||
async for msg in message.client.iter_messages(
|
||||
entity="t.me/" + self.config["STICKERS_USERNAME"],
|
||||
min_id=first.id,
|
||||
reverse=True,
|
||||
):
|
||||
msgs += [msg.id]
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
msgs + [first],
|
||||
)
|
||||
return
|
||||
uploaded = await message.client.upload_file(
|
||||
img, file_name="AnimatedSticker.tgs"
|
||||
)
|
||||
m1 = await conv.send_file(uploaded, force_document=False)
|
||||
m2 = await conv.send_message(emojis)
|
||||
await conv.send_message("/done")
|
||||
# Block now so that we mark it all as read
|
||||
await message.client.send_read_acknowledge(conv.chat_id)
|
||||
r1 = await conv.get_response(m1)
|
||||
r2 = await conv.get_response(m2)
|
||||
if "/done" not in r2.message:
|
||||
# That's an error
|
||||
logger.error("Bad response from StickerBot 1")
|
||||
logger.error(r0)
|
||||
logger.error(r1)
|
||||
logger.error(r2)
|
||||
await utils.answer(
|
||||
message, self.strings("internal_error", message)
|
||||
)
|
||||
return
|
||||
msgs = []
|
||||
async for msg in message.client.iter_messages(
|
||||
entity="t.me/" + self.config["STICKERS_USERNAME"],
|
||||
min_id=first.id,
|
||||
reverse=True,
|
||||
):
|
||||
msgs += [msg.id]
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"], msgs + [first]
|
||||
)
|
||||
if "emoji" in r2.message:
|
||||
# The emoji(s) are invalid.
|
||||
logger.error("Bad response from StickerBot 2")
|
||||
logger.error(r2)
|
||||
await utils.answer(message, self.strings("bad_emojis", message))
|
||||
return
|
||||
|
||||
else:
|
||||
try:
|
||||
thumb = BytesIO()
|
||||
task = asyncio.ensure_future(
|
||||
utils.run_sync(
|
||||
resize_image, img, self.config["STICKER_SIZE"], thumb
|
||||
)
|
||||
)
|
||||
thumb.name = "sticker.png"
|
||||
# The data is now in thumb.
|
||||
# Lock access to @Stickers
|
||||
async with self._lock:
|
||||
# Without t.me/ there is ambiguity; Stickers could be a name,
|
||||
# in which case the wrong entity could be returned
|
||||
# TODO should this be translated?
|
||||
conv = message.client.conversation(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
timeout=5,
|
||||
exclusive=True,
|
||||
)
|
||||
async with conv:
|
||||
first = await conv.send_message("/cancel")
|
||||
await conv.get_response()
|
||||
await conv.send_message("/addsticker")
|
||||
r0 = await conv.get_response()
|
||||
buttons = r0.buttons
|
||||
if buttons is not None:
|
||||
logger.debug("there are buttons, good")
|
||||
button = click_buttons(buttons, args[0])
|
||||
m0 = await button.click()
|
||||
elif "/newpack" in r0.message:
|
||||
await utils.answer(
|
||||
message, self.strings("new_pack", message)
|
||||
)
|
||||
return
|
||||
else:
|
||||
logger.warning("there's no buttons!")
|
||||
m0 = await message.client.send_message(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
"/cancel",
|
||||
)
|
||||
await utils.answer(
|
||||
message, self.strings("internal_error", message)
|
||||
)
|
||||
return
|
||||
# We have sent the pack we wish to modify.
|
||||
# Upload sticker
|
||||
r0 = await conv.get_response()
|
||||
if ".TGS" in r0.message:
|
||||
logger.error("bad response from stickerbot 0")
|
||||
logger.error(r0)
|
||||
await utils.answer(
|
||||
message, self.strings("animated_pack", message)
|
||||
)
|
||||
msgs = []
|
||||
async for msg in message.client.iter_messages(
|
||||
entity="t.me/" + self.config["STICKERS_USERNAME"],
|
||||
min_id=first.id,
|
||||
reverse=True,
|
||||
):
|
||||
msgs += [msg.id]
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
msgs + [first],
|
||||
)
|
||||
return
|
||||
if "120" in r0.message:
|
||||
logger.error("bad response from stickerbot 0")
|
||||
logger.error(r0)
|
||||
await utils.answer(
|
||||
message, self.strings("pack_full", message)
|
||||
)
|
||||
msgs = []
|
||||
async for msg in message.client.iter_messages(
|
||||
entity="t.me/" + self.config["STICKERS_USERNAME"],
|
||||
min_id=first.id,
|
||||
reverse=True,
|
||||
):
|
||||
if msg.id != m0.id:
|
||||
msgs += [msg.id]
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"],
|
||||
msgs + [first],
|
||||
)
|
||||
return
|
||||
await task # We can resize the thumbnail while the sticker bot is processing other data
|
||||
thumb.seek(0)
|
||||
m1 = await conv.send_file(
|
||||
thumb, allow_cache=False, force_document=True
|
||||
)
|
||||
r1 = await conv.get_response(m1)
|
||||
m2 = await conv.send_message(emojis)
|
||||
r2 = await conv.get_response(m2)
|
||||
if "/done" in r2.message:
|
||||
await conv.send_message("/done")
|
||||
else:
|
||||
logger.error(r1)
|
||||
logger.error(r2)
|
||||
logger.error("Bad response from StickerBot 0")
|
||||
await utils.answer(
|
||||
message, self.strings("internal_error", message)
|
||||
)
|
||||
await message.client.send_read_acknowledge(conv.chat_id)
|
||||
if "/done" not in r2.message:
|
||||
# That's an error
|
||||
logger.error("Bad response from StickerBot 1")
|
||||
logger.error(r1)
|
||||
logger.error(r2)
|
||||
await utils.answer(
|
||||
message, self.strings("internal_error", message)
|
||||
)
|
||||
return
|
||||
msgs = []
|
||||
async for msg in message.client.iter_messages(
|
||||
entity="t.me/" + self.config["STICKERS_USERNAME"],
|
||||
min_id=first.id,
|
||||
reverse=True,
|
||||
):
|
||||
msgs += [msg.id]
|
||||
logger.debug(msgs)
|
||||
await message.client.delete_messages(
|
||||
"t.me/" + self.config["STICKERS_USERNAME"], msgs + [first]
|
||||
)
|
||||
if "emoji" in r2.message:
|
||||
# The emoji(s) are invalid.
|
||||
logger.error("Bad response from StickerBot 2")
|
||||
logger.error(r2)
|
||||
await utils.answer(
|
||||
message, self.strings("bad_emojis", message)
|
||||
)
|
||||
return
|
||||
finally:
|
||||
thumb.close()
|
||||
finally:
|
||||
img.close()
|
||||
packurl = utils.escape_html("https://t.me/addstickers/{}".format(button.text))
|
||||
await utils.answer(message, self.strings("added", message).format(packurl))
|
||||
|
||||
async def gififycmd(self, message):
|
||||
"""Convert the replied animated sticker to a GIF"""
|
||||
args = utils.get_args(message)
|
||||
fps = 5
|
||||
quality = 256
|
||||
try:
|
||||
if len(args) == 1:
|
||||
fps = int(args[0])
|
||||
elif len(args) == 2:
|
||||
quality = int(args[0])
|
||||
fps = int(args[1])
|
||||
except ValueError:
|
||||
logger.exception("Failed to parse quality/fps")
|
||||
target = await message.get_reply_message()
|
||||
if (
|
||||
target is None
|
||||
or target.file is None
|
||||
or target.file.mime_type != "application/x-tgsticker"
|
||||
):
|
||||
await utils.answer(message, self.strings("bad_animated_sticker", message))
|
||||
return
|
||||
try:
|
||||
file = BytesIO()
|
||||
await target.download_media(file)
|
||||
file.seek(0)
|
||||
anim = await utils.run_sync(lottie.parsers.tgs.parse_tgs, file)
|
||||
file.close()
|
||||
result = BytesIO()
|
||||
result.name = "animation.gif"
|
||||
await utils.run_sync(
|
||||
lottie.exporters.gif.export_gif, anim, result, quality, fps
|
||||
)
|
||||
result.seek(0)
|
||||
await utils.answer(message, result)
|
||||
finally:
|
||||
try:
|
||||
file.close()
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
try:
|
||||
result.close()
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
|
||||
async def stextcmd(self, message):
|
||||
""".stext <reply to photo>"""
|
||||
await message.delete()
|
||||
text = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
if not text:
|
||||
if not reply:
|
||||
text = "#ffffff .stext <text or reply>"
|
||||
elif not reply.message:
|
||||
text = "#ffffff .stext <text or reply>"
|
||||
else:
|
||||
text = reply.raw_text
|
||||
color = text.split(" ", 1)[0]
|
||||
if color.startswith("#") and len(color) == 7:
|
||||
for ch in color.lower()[1:]:
|
||||
if ch not in "0123456789abcdef":
|
||||
break
|
||||
if len(text.split(" ", 1)) > 1:
|
||||
text = text.split(" ", 1)[1]
|
||||
else:
|
||||
if reply:
|
||||
if reply.message:
|
||||
text = reply.raw_text
|
||||
else:
|
||||
color = "#FFFFFF"
|
||||
txt = []
|
||||
for line in text.split("\n"):
|
||||
txt.append("\n".join(wrap(line, 30)))
|
||||
text = "\n".join(txt)
|
||||
font = io.BytesIO(bytes_font)
|
||||
font = ImageFont.truetype(font, 100)
|
||||
image = Image.new("RGBA", (1, 1), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
w, h = draw.multiline_textsize(text=text, font=font)
|
||||
image = Image.new("RGBA", (w + 100, h + 100), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.multiline_text((50, 50), text=text, font=font, fill=color, align="center")
|
||||
output = io.BytesIO()
|
||||
output.name = color + ".webp"
|
||||
image.save(output, "WEBP")
|
||||
output.seek(0)
|
||||
await self.client.send_file(message.to_id, output, reply_to=reply)
|
||||
|
||||
|
||||
def click_buttons(buttons, target_pack):
|
||||
buttons = list(itertools.chain.from_iterable(buttons))
|
||||
# Process in reverse order; most difficult to match first
|
||||
try:
|
||||
return buttons[int(target_pack)]
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
logger.debug(buttons)
|
||||
for button in buttons:
|
||||
logger.debug(button)
|
||||
if button.text == target_pack:
|
||||
return button
|
||||
for button in buttons:
|
||||
if target_pack in button.text:
|
||||
return button
|
||||
for button in buttons:
|
||||
if target_pack.lower() in button.text.lower():
|
||||
return button
|
||||
return buttons[-1]
|
||||
|
||||
|
||||
def resize_image(img, size, dest):
|
||||
# Wrapper for asyncio purposes
|
||||
try:
|
||||
im = Image.open(img)
|
||||
# We used to use thumbnail(size) here, but it returns with a *max* dimension of 512,512
|
||||
# rather than making one side exactly 512 so we have to calculate dimensions manually :(
|
||||
if im.width == im.height:
|
||||
size = (512, 512)
|
||||
elif im.width < im.height:
|
||||
size = (int(512 * im.width / im.height), 512)
|
||||
else:
|
||||
size = (512, int(512 * im.height / im.width))
|
||||
logger.debug("Resizing to %s", size)
|
||||
im.resize(size).save(dest, "PNG")
|
||||
finally:
|
||||
im.close()
|
||||
img.close()
|
||||
del im
|
||||
58
GeekTG/FTG-Modules/tags.py
Normal file
58
GeekTG/FTG-Modules/tags.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd
|
||||
|
||||
import random
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TagMod(loader.Module):
|
||||
"""Secretly tag a user"""
|
||||
|
||||
strings = {"name": "Tags"}
|
||||
|
||||
async def tagcmd(self, message):
|
||||
""".tag <@> <text>."""
|
||||
args = utils.get_args_raw(message).split(" ")
|
||||
reply = await message.get_reply_message()
|
||||
user, tag = None, None
|
||||
try:
|
||||
if len(args) == 1:
|
||||
args = utils.get_args_raw(message)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
tag = "Hey"
|
||||
elif len(args) >= 2:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
tag = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
except:
|
||||
return await message.edit("Failed to find a user.")
|
||||
await message.delete()
|
||||
await message.client.send_message(
|
||||
message.to_id,
|
||||
f'{tag} <a href="tg://user?id={user.id}">\u2060</a>',
|
||||
reply_to=reply.id if reply else None,
|
||||
)
|
||||
|
||||
async def tagallcmd(self, message):
|
||||
""".tagall <text> - tag all users in chat"""
|
||||
args = utils.get_args_raw(message)
|
||||
tag = args or "Hey"
|
||||
await message.delete()
|
||||
tags = []
|
||||
async for user in message.client.iter_participants(message.to_id):
|
||||
tags.append(f"<a href='tg://user?id={user.id}'>\u2060</a>")
|
||||
chunkss = list(chunks(tags, 5))
|
||||
random.shuffle(chunkss)
|
||||
for chunk in chunkss:
|
||||
await message.client.send_message(message.to_id, tag + "\u2060".join(chunk))
|
||||
|
||||
|
||||
def chunks(lst, n):
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i : i + n]
|
||||
388
GeekTG/FTG-Modules/terminal.py
Normal file
388
GeekTG/FTG-Modules/terminal.py
Normal file
@@ -0,0 +1,388 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
import telethon
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TerminalMod(loader.Module):
|
||||
"""Runs commands"""
|
||||
|
||||
strings = {
|
||||
"name": "Terminal",
|
||||
"flood_wait_protect_cfg_doc": "How long to wait in seconds between edits in commands",
|
||||
"what_to_kill": "<b>Reply to a terminal command to terminate it</b>",
|
||||
"kill_fail": "<b>Could not kill process</b>",
|
||||
"killed": "<b>Killed</b>",
|
||||
"no_cmd": "<b>No command is running in that message</b>",
|
||||
"running": "<b>Command:</b> <code>{}</code>",
|
||||
"finished": "\n<b>Code:</b> <code>{}</code>",
|
||||
"stdout": "\n<b>Stdout:</b>\n<code>",
|
||||
"stderr": "</code>\n\n<b>Stderr:</b>\n<code>",
|
||||
"end": "</code>",
|
||||
"auth_fail": "<b>Authentication failed, please try again</b>",
|
||||
"auth_needed": '<a href="tg://user?id={}">Interactive authentication required</a>',
|
||||
"auth_msg": (
|
||||
"<b>Please edit this message to the password for</b> "
|
||||
"<code>{}</code> <b>to run</b> <code>{}</code>"
|
||||
),
|
||||
"auth_locked": "<b>Authentication failed, please try again later</b>",
|
||||
"auth_ongoing": "<b>Authenticating...</b>",
|
||||
"done": "<b>Done</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"FLOOD_WAIT_PROTECT",
|
||||
2,
|
||||
lambda m: self.strings("flood_wait_protect_cfg_doc", m),
|
||||
)
|
||||
self.activecmds = {}
|
||||
|
||||
@loader.owner
|
||||
async def terminalcmd(self, message):
|
||||
""".terminal <command>"""
|
||||
await self.run_command(message, utils.get_args_raw(message))
|
||||
|
||||
@loader.owner
|
||||
async def aptcmd(self, message):
|
||||
"""Shorthand for '.terminal apt'"""
|
||||
await self.run_command(
|
||||
message,
|
||||
("apt " if os.geteuid() == 0 else "sudo -S apt ")
|
||||
+ utils.get_args_raw(message)
|
||||
+ " -y",
|
||||
RawMessageEditor(
|
||||
message,
|
||||
"apt " + utils.get_args_raw(message),
|
||||
self.config,
|
||||
self.strings,
|
||||
message,
|
||||
True,
|
||||
),
|
||||
)
|
||||
|
||||
async def run_command(self, message, cmd, editor=None):
|
||||
if len(cmd.split(" ")) > 1 and cmd.split(" ")[0] == "sudo":
|
||||
needsswitch = True
|
||||
for word in cmd.split(" ", 1)[1].split(" "):
|
||||
if word[0] != "-":
|
||||
break
|
||||
if word == "-S":
|
||||
needsswitch = False
|
||||
if needsswitch:
|
||||
cmd = " ".join([cmd.split(" ", 1)[0], "-S", cmd.split(" ", 1)[1]])
|
||||
sproc = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=utils.get_base_dir(),
|
||||
)
|
||||
if editor is None:
|
||||
editor = SudoMessageEditor(message, cmd, self.config, self.strings, message)
|
||||
editor.update_process(sproc)
|
||||
self.activecmds[hash_msg(message)] = sproc
|
||||
await editor.redraw()
|
||||
await asyncio.gather(
|
||||
read_stream(
|
||||
editor.update_stdout, sproc.stdout, self.config["FLOOD_WAIT_PROTECT"]
|
||||
),
|
||||
read_stream(
|
||||
editor.update_stderr, sproc.stderr, self.config["FLOOD_WAIT_PROTECT"]
|
||||
),
|
||||
)
|
||||
await editor.cmd_ended(await sproc.wait())
|
||||
del self.activecmds[hash_msg(message)]
|
||||
|
||||
@loader.owner
|
||||
async def terminatecmd(self, message):
|
||||
"""Use in reply to send SIGTERM to a process"""
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("what_to_kill", message))
|
||||
return
|
||||
if hash_msg(await message.get_reply_message()) in self.activecmds:
|
||||
try:
|
||||
self.activecmds[hash_msg(await message.get_reply_message())].terminate()
|
||||
except Exception:
|
||||
logger.exception("Killing process failed")
|
||||
await utils.answer(message, self.strings("kill_fail", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("killed", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("no_cmd", message))
|
||||
|
||||
@loader.owner
|
||||
async def killcmd(self, message):
|
||||
"""Use in reply to send SIGKILL to a process"""
|
||||
if not message.is_reply:
|
||||
await utils.answer(message, self.strings("what_to_kill", message))
|
||||
return
|
||||
if hash_msg(await message.get_reply_message()) in self.activecmds:
|
||||
try:
|
||||
self.activecmds[hash_msg(await message.get_reply_message())].kill()
|
||||
except Exception:
|
||||
logger.exception("Killing process failed")
|
||||
await utils.answer(message, self.strings("kill_fail", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("killed", message))
|
||||
else:
|
||||
await utils.answer(message, self.strings("no_cmd", message))
|
||||
|
||||
async def neofetchcmd(self, message):
|
||||
"""Show system stats via neofetch"""
|
||||
await self.run_command(
|
||||
message,
|
||||
"neofetch --stdout",
|
||||
RawMessageEditor(
|
||||
message, "neofetch --stdout", self.config, self.strings, message
|
||||
),
|
||||
)
|
||||
|
||||
async def uptimecmd(self, message):
|
||||
"""Show system uptime"""
|
||||
await self.run_command(
|
||||
message,
|
||||
"uptime",
|
||||
RawMessageEditor(message, "uptime", self.config, self.strings, message),
|
||||
)
|
||||
|
||||
|
||||
def hash_msg(message):
|
||||
return str(utils.get_chat_id(message)) + "/" + str(message.id)
|
||||
|
||||
|
||||
async def read_stream(func, stream, delay):
|
||||
last_task = None
|
||||
data = b""
|
||||
while True:
|
||||
dat = await stream.read(1)
|
||||
if not dat:
|
||||
# EOF
|
||||
if last_task:
|
||||
# Send all pending data
|
||||
last_task.cancel()
|
||||
await func(data.decode("utf-8"))
|
||||
# If there is no last task there is inherently no data, so theres no point sending a blank string
|
||||
break
|
||||
data += dat
|
||||
if last_task:
|
||||
last_task.cancel()
|
||||
last_task = asyncio.ensure_future(sleep_for_task(func, data, delay))
|
||||
|
||||
|
||||
async def sleep_for_task(func, data, delay):
|
||||
await asyncio.sleep(delay)
|
||||
await func(data.decode("utf-8"))
|
||||
|
||||
|
||||
class MessageEditor:
|
||||
def __init__(self, message, command, config, strings, request_message):
|
||||
self.message = [message]
|
||||
self.command = command
|
||||
self.stdout = ""
|
||||
self.stderr = ""
|
||||
self.rc = None
|
||||
self.redraws = 0
|
||||
self.config = config
|
||||
self.strings = strings
|
||||
self.request_message = request_message
|
||||
|
||||
async def update_stdout(self, stdout):
|
||||
self.stdout = stdout
|
||||
await self.redraw()
|
||||
|
||||
async def update_stderr(self, stderr):
|
||||
self.stderr = stderr
|
||||
await self.redraw()
|
||||
|
||||
async def redraw(self):
|
||||
text = self.strings("running", self.request_message).format(
|
||||
utils.escape_html(self.command)
|
||||
)
|
||||
if self.rc is not None:
|
||||
text += self.strings("finished", self.request_message).format(
|
||||
utils.escape_html(str(self.rc))
|
||||
)
|
||||
text += self.strings("stdout", self.request_message)
|
||||
text += utils.escape_html(self.stdout[max(len(self.stdout) - 2048, 0) :])
|
||||
text += self.strings("stderr", self.request_message)
|
||||
text += utils.escape_html(self.stderr[max(len(self.stderr) - 1024, 0) :])
|
||||
text += self.strings("end", self.request_message)
|
||||
try:
|
||||
self.message = await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
pass
|
||||
except telethon.errors.rpcerrorlist.MessageTooLongError as e:
|
||||
logger.error(e)
|
||||
logger.error(text)
|
||||
|
||||
# The message is never empty due to the template header
|
||||
|
||||
async def cmd_ended(self, rc):
|
||||
self.rc = rc
|
||||
self.state = 4
|
||||
await self.redraw()
|
||||
|
||||
def update_process(self, process):
|
||||
pass
|
||||
|
||||
|
||||
class SudoMessageEditor(MessageEditor):
|
||||
# Let's just hope these are safe to parse
|
||||
PASS_REQ = "[sudo] password for"
|
||||
WRONG_PASS = r"\[sudo\] password for (.*): Sorry, try again\."
|
||||
TOO_MANY_TRIES = (
|
||||
r"\[sudo\] password for (.*): sudo: [0-9]+ incorrect password attempts"
|
||||
)
|
||||
|
||||
def __init__(self, message, command, config, strings, request_message):
|
||||
super().__init__(message, command, config, strings, request_message)
|
||||
self.process = None
|
||||
self.state = 0
|
||||
self.authmsg = None
|
||||
|
||||
def update_process(self, process):
|
||||
logger.debug("got sproc obj %s", process)
|
||||
self.process = process
|
||||
|
||||
async def update_stderr(self, stderr):
|
||||
logger.debug("stderr update " + stderr)
|
||||
self.stderr = stderr
|
||||
lines = stderr.strip().split("\n")
|
||||
lastline = lines[-1]
|
||||
lastlines = lastline.rsplit(" ", 1)
|
||||
handled = False
|
||||
if (
|
||||
len(lines) > 1
|
||||
and re.fullmatch(self.WRONG_PASS, lines[-2])
|
||||
and lastlines[0] == self.PASS_REQ
|
||||
and self.state == 1
|
||||
):
|
||||
logger.debug("switching state to 0")
|
||||
await self.authmsg.edit(self.strings("auth_failed", self.request_message))
|
||||
self.state = 0
|
||||
handled = True
|
||||
await asyncio.sleep(2)
|
||||
await self.authmsg.delete()
|
||||
if lastlines[0] == self.PASS_REQ and self.state == 0:
|
||||
logger.debug("Success to find sudo log!")
|
||||
text = self.strings("auth_needed", self.request_message).format(
|
||||
(await self.message[0].client.get_me()).id
|
||||
)
|
||||
try:
|
||||
await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError as e:
|
||||
logger.debug(e)
|
||||
logger.debug("edited message with link to self")
|
||||
command = "<code>" + utils.escape_html(self.command) + "</code>"
|
||||
user = utils.escape_html(lastlines[1][:-1])
|
||||
self.authmsg = await self.message[0].client.send_message(
|
||||
"me",
|
||||
self.strings("auth_msg", self.request_message).format(command, user),
|
||||
)
|
||||
logger.debug("sent message to self")
|
||||
self.message[0].client.remove_event_handler(self.on_message_edited)
|
||||
self.message[0].client.add_event_handler(
|
||||
self.on_message_edited,
|
||||
telethon.events.messageedited.MessageEdited(chats=["me"]),
|
||||
)
|
||||
logger.debug("registered handler")
|
||||
handled = True
|
||||
if (
|
||||
len(lines) > 1
|
||||
and re.fullmatch(self.TOO_MANY_TRIES, lastline)
|
||||
and self.state in [1, 3, 4]
|
||||
):
|
||||
logger.debug("password wrong lots of times")
|
||||
await utils.answer(
|
||||
self.message, self.strings("auth_locked", self.request_message)
|
||||
)
|
||||
await self.authmsg.delete()
|
||||
self.state = 2
|
||||
handled = True
|
||||
if not handled:
|
||||
logger.debug("Didn't find sudo log.")
|
||||
if self.authmsg is not None:
|
||||
await self.authmsg[0].delete()
|
||||
self.authmsg = None
|
||||
self.state = 2
|
||||
await self.redraw()
|
||||
logger.debug(self.state)
|
||||
|
||||
async def update_stdout(self, stdout):
|
||||
self.stdout = stdout
|
||||
if self.state != 2:
|
||||
self.state = 3 # Means that we got stdout only
|
||||
if self.authmsg is not None:
|
||||
await self.authmsg.delete()
|
||||
self.authmsg = None
|
||||
await self.redraw()
|
||||
|
||||
async def on_message_edited(self, message):
|
||||
# Message contains sensitive information.
|
||||
if self.authmsg is None:
|
||||
return
|
||||
logger.debug("got message edit update in self " + str(message.id))
|
||||
if hash_msg(message) == hash_msg(self.authmsg):
|
||||
# The user has provided interactive authentication. Send password to stdin for sudo.
|
||||
try:
|
||||
self.authmsg = await utils.answer(
|
||||
message, self.strings("auth_ongoing", self.request_message)
|
||||
)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
# Try to clear personal info if the edit fails
|
||||
await message.delete()
|
||||
self.state = 1
|
||||
self.process.stdin.write(
|
||||
message.message.message.split("\n", 1)[0].encode("utf-8") + b"\n"
|
||||
)
|
||||
|
||||
|
||||
class RawMessageEditor(SudoMessageEditor):
|
||||
def __init__(
|
||||
self, message, command, config, strings, request_message, show_done=False
|
||||
):
|
||||
super().__init__(message, command, config, strings, request_message)
|
||||
self.show_done = show_done
|
||||
|
||||
async def redraw(self):
|
||||
logger.debug(self.rc)
|
||||
if self.rc is None:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stdout[max(len(self.stdout) - 4095, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
elif self.rc == 0:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stdout[max(len(self.stdout) - 4090, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
else:
|
||||
text = (
|
||||
"<code>"
|
||||
+ utils.escape_html(self.stderr[max(len(self.stderr) - 4095, 0) :])
|
||||
+ "</code>"
|
||||
)
|
||||
if self.rc is not None and self.show_done:
|
||||
text += "\n" + self.strings("done", self.request_message)
|
||||
logger.debug(text)
|
||||
try:
|
||||
await utils.answer(self.message, text)
|
||||
except telethon.errors.rpcerrorlist.MessageNotModifiedError:
|
||||
pass
|
||||
except (telethon.errors.rpcerrorlist.MessageEmptyError, ValueError):
|
||||
pass
|
||||
except telethon.errors.rpcerrorlist.MessageTooLongError as e:
|
||||
logger.error(e)
|
||||
logger.error(text)
|
||||
74
GeekTG/FTG-Modules/text_generator.py
Normal file
74
GeekTG/FTG-Modules/text_generator.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# By @vreply @pernel_kanic @nim1love @db0mb3r and geyporn by @tshipenchko
|
||||
|
||||
import requests
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
def register(cb):
|
||||
cb(TextGeneratorMod())
|
||||
|
||||
|
||||
class TextGeneratorMod(loader.Module):
|
||||
"Generating text using machine learning"
|
||||
|
||||
strings = {
|
||||
"name": "TextGenerator",
|
||||
"no_text": "<strong>Empty message</strong>",
|
||||
"wait": "<strong>Generating text...</strong>",
|
||||
}
|
||||
|
||||
async def pfcmd(self, message):
|
||||
"""Generates text with Porfirevich: porfirevich.ru"""
|
||||
text = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if text:
|
||||
if reply:
|
||||
text = reply.raw_text + text
|
||||
elif reply:
|
||||
text = reply.raw_text
|
||||
else:
|
||||
return await utils.answer(message, self.strings("no_text", message))
|
||||
|
||||
message = await utils.answer(message, self.strings("wait", message))
|
||||
response = (
|
||||
await utils.run_sync(
|
||||
requests.post,
|
||||
"https://pelevin.gpt.dobro.ai/generate/",
|
||||
json={"prompt": text, "length": 30},
|
||||
)
|
||||
).json()
|
||||
|
||||
return await utils.answer(
|
||||
message, f"<strong>{text}</strong>" + response["replies"][-1]
|
||||
)
|
||||
|
||||
async def gptcmd(self, message):
|
||||
"""Generates text with ruGPT-3 XL: russiannlp.github.io/rugpt-demo/"""
|
||||
text = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
|
||||
if text:
|
||||
if reply:
|
||||
text = reply.raw_text + text
|
||||
elif reply:
|
||||
text = reply.raw_text
|
||||
else:
|
||||
return await utils.answer(message, self.strings("no_text", message))
|
||||
|
||||
message = await utils.answer(message, self.strings("wait", message))
|
||||
response = (
|
||||
await utils.run_sync(
|
||||
requests.post,
|
||||
"https://api.aicloud.sbercloud.ru/public/v1/public_inference/gpt3/predict",
|
||||
json={"text": text},
|
||||
)
|
||||
).json()
|
||||
return await utils.answer(
|
||||
message,
|
||||
"<strong>"
|
||||
+ text
|
||||
+ "</strong>"
|
||||
+ response["predictions"].split(text.split()[-1], maxsplit=1)[1],
|
||||
)
|
||||
84
GeekTG/FTG-Modules/translate.py
Normal file
84
GeekTG/FTG-Modules/translate.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd
|
||||
|
||||
# requires: googletrans==4.0.0rc1
|
||||
|
||||
from googletrans import LANGUAGES, Translator
|
||||
from telethon import events, functions
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class TranslatorMod(loader.Module):
|
||||
"""Translator Module"""
|
||||
|
||||
strings = {"name": "Translate"}
|
||||
|
||||
async def gtrslcmd(self, message):
|
||||
"""Use it: .gtrsl <what language to translate from> <to which language to translate>
|
||||
<text> or .gtrsl <to translate> <reply>; langs"""
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
langs = LANGUAGES
|
||||
lang = args.split()
|
||||
tr = Translator().translate
|
||||
if not args and not reply:
|
||||
return await message.edit("No arguments or reply")
|
||||
if args == "langs":
|
||||
return await message.edit(
|
||||
"<code>" + "\n".join(str(langs).split(", ")) + "</code>"
|
||||
)
|
||||
if reply:
|
||||
try:
|
||||
trslreply = True
|
||||
text = reply.text
|
||||
if len(lang) >= 2:
|
||||
trslreply = False
|
||||
dest = langs[lang[0]]
|
||||
r = tr(args.split(" ", 1)[1] if not trslreply else text, dest=dest)
|
||||
except:
|
||||
r = tr(reply.text)
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
src = langs[lang[0]]
|
||||
dest = langs[lang[1]]
|
||||
text = args.split(" ", 2)[2]
|
||||
r = tr(text, src=src, dest=dest)
|
||||
except:
|
||||
dest = langs[lang[0]]
|
||||
text = args.split(" ", 1)[1]
|
||||
r = tr(text, dest=dest)
|
||||
except KeyError:
|
||||
r = tr(args)
|
||||
return await message.edit(f"<b>[{r.src} ➜ {r.dest}]</b>\n{r.text}")
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def translatecmd(self, message):
|
||||
"""Translate text via Yandex Translate"""
|
||||
chat = "@YTranslateBot"
|
||||
reply = await message.get_reply_message()
|
||||
async with message.client.conversation(chat) as conv:
|
||||
text = utils.get_args_raw(message)
|
||||
if reply:
|
||||
text = await message.get_reply_message()
|
||||
try:
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=104784211)
|
||||
)
|
||||
mm = await message.client.send_message(chat, text)
|
||||
response = await response
|
||||
await mm.delete()
|
||||
except YouBlockedUserError:
|
||||
await message.edit("<code>Unblock @YTranslateBot</code>")
|
||||
return
|
||||
await message.edit(str(response.text).split(": ", 1)[1])
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer="YTranslateBot", max_id=0, just_clear=False, revoke=True
|
||||
)
|
||||
)
|
||||
123
GeekTG/FTG-Modules/tts.py
Normal file
123
GeekTG/FTG-Modules/tts.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# requires: pydub requests gtts hachoir
|
||||
import io
|
||||
import os
|
||||
|
||||
import requests
|
||||
from gtts import gTTS
|
||||
from pydub import AudioSegment
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
def register(cb):
|
||||
cb(DttsMod())
|
||||
|
||||
|
||||
class DttsMod(loader.Module):
|
||||
"""Text to speech module"""
|
||||
|
||||
strings = {
|
||||
"name": "DTTS",
|
||||
"no_text": "I can't say nothing",
|
||||
"tts_lang_cfg": "Set your language code for the TTS here.",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
"TTS_LANG", "en", lambda m: self.strings("tts_lang_cfg", m)
|
||||
)
|
||||
self.is_ffmpeg = os.system("ffmpeg -version") == 0
|
||||
|
||||
async def say(self, message, speaker, text, file=".dtts.mp3"):
|
||||
reply = await message.get_reply_message()
|
||||
if not text:
|
||||
if not reply:
|
||||
return await utils.answer(message, self.strings["no_text"])
|
||||
text = reply.raw_text # use text from reply
|
||||
if not text:
|
||||
return await utils.answer(message, self.strings["no_text"])
|
||||
if message.out:
|
||||
await message.delete() # Delete message only one is user's
|
||||
data = {"text": text}
|
||||
if speaker:
|
||||
data["speaker"] = speaker
|
||||
|
||||
# creating file in memory
|
||||
f = io.BytesIO(
|
||||
requests.get("https://station.aimylogic.com/generate", data=data).content
|
||||
)
|
||||
f.name = file
|
||||
|
||||
if self.is_ffmpeg:
|
||||
f, duration = to_voice(f)
|
||||
else:
|
||||
duration = None
|
||||
|
||||
await message.client.send_file(
|
||||
message.chat_id, f, voice_note=True, reply_to=reply, duration=duration
|
||||
)
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def levitancmd(self, message):
|
||||
"""Convert text to speech with levitan voice"""
|
||||
await self.say(message, "levitan", utils.get_args_raw(message))
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def oksanacmd(self, message):
|
||||
"""Convert text to speech with oksana voice"""
|
||||
await self.say(message, "oksana", utils.get_args_raw(message))
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def yandexcmd(self, message):
|
||||
"""Convert text to speech with yandex voice"""
|
||||
await self.say(message, None, utils.get_args_raw(message))
|
||||
|
||||
@loader.unrestricted
|
||||
@loader.ratelimit
|
||||
async def ttscmd(self, message):
|
||||
"""Convert text to speech with Google APIs"""
|
||||
reply = await message.get_reply_message()
|
||||
text = utils.get_args_raw(message.message)
|
||||
|
||||
if not text:
|
||||
if message.is_reply:
|
||||
text = (await message.get_reply_message()).message
|
||||
else:
|
||||
return await utils.answer(message, self.strings("no_text", message))
|
||||
|
||||
if message.out:
|
||||
await message.delete()
|
||||
|
||||
tts = await utils.run_sync(gTTS, text, lang=self.config["TTS_LANG"])
|
||||
voice = io.BytesIO()
|
||||
await utils.run_sync(tts.write_to_fp, voice)
|
||||
voice.seek(0)
|
||||
voice.name = "voice.mp3"
|
||||
|
||||
if self.is_ffmpeg:
|
||||
voice, duration = to_voice(voice)
|
||||
else:
|
||||
duration = None
|
||||
|
||||
await message.client.send_file(
|
||||
message.chat_id, voice, voice_note=True, reply_to=reply, duration=duration
|
||||
)
|
||||
|
||||
|
||||
def to_voice(item):
|
||||
"""Returns audio in opus format and it's duration"""
|
||||
item.seek(0)
|
||||
item = AudioSegment.from_file(item)
|
||||
m = io.BytesIO()
|
||||
m.name = "voice.ogg"
|
||||
item.split_to_mono()
|
||||
dur = len(item) / 1000
|
||||
item.export(m, format="ogg", bitrate="64k", codec="libopus")
|
||||
m.seek(0)
|
||||
return m, dur
|
||||
|
||||
|
||||
# By @vreply @pernel_kanic @nim1love @db0mb3r and add @tshipenchko some geyporn
|
||||
139
GeekTG/FTG-Modules/url.py
Normal file
139
GeekTG/FTG-Modules/url.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @GovnoCodules, @ftgmodulesbyfl1yd
|
||||
|
||||
import logging
|
||||
import os
|
||||
import urllib
|
||||
|
||||
from requests import post
|
||||
from telethon import events, functions
|
||||
from telethon.errors.rpcerrorlist import YouBlockedUserError
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class URlMod(loader.Module):
|
||||
"""URL Module"""
|
||||
|
||||
strings = {
|
||||
"name": "URL",
|
||||
"some_rong": "<b>You're doing something wrong!\\write</b> <code>.help "
|
||||
+ "gg.gg</code> <b>for information.</b>",
|
||||
"result": "<b>Here you go, help yourself.</b>\n<a href='{}'>{}</a>",
|
||||
"default": "How to use Google?",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
|
||||
async def ggcmd(self, message):
|
||||
""".gg <link or reply_to_link>"""
|
||||
m_text = utils.get_args_raw(message)
|
||||
if not m_text:
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await utils.answer(message, self.strings("some_rong", message))
|
||||
return
|
||||
long_url = reply.raw_text
|
||||
else:
|
||||
long_url = m_text
|
||||
|
||||
if "http://" not in long_url and "https://" not in long_url:
|
||||
long_url = f"http://{long_url}"
|
||||
t_check = f"URL: {long_url}\nCheck..."
|
||||
await utils.answer(message, t_check)
|
||||
check = post(
|
||||
"http://gg.gg/check",
|
||||
data={
|
||||
"custom_path": None,
|
||||
"use_norefs": "0",
|
||||
"long_url": long_url,
|
||||
"app": "site",
|
||||
"version": "0.1",
|
||||
},
|
||||
).text
|
||||
if check != "ok":
|
||||
await utils.answer(message, check)
|
||||
return
|
||||
await utils.answer(message, "Create...")
|
||||
short = post(
|
||||
"http://gg.gg/create",
|
||||
data={
|
||||
"custom_path": None,
|
||||
"use_norefs": "0",
|
||||
"long_url": long_url,
|
||||
"app": "site",
|
||||
"version": "0.1",
|
||||
},
|
||||
).text
|
||||
await utils.answer(message, short)
|
||||
|
||||
async def lgtcmd(self, message):
|
||||
"""Shorten the link using the verylegit.link service"""
|
||||
args = utils.get_args_raw(message)
|
||||
if not args:
|
||||
return await message.edit("No arguments")
|
||||
link = os.popen(f"curl verylegit.link/sketchify -d long_url={args}").read()
|
||||
await message.edit(f"Ссылка:\n> {link}")
|
||||
|
||||
async def clckcmd(self, message):
|
||||
"""Shorten the link using the service clck.ru"""
|
||||
m_text = utils.get_args_raw(message)
|
||||
if not m_text:
|
||||
reply = await message.get_reply_message()
|
||||
if not reply:
|
||||
await utils.answer(message, self.strings("some_rong", message))
|
||||
return
|
||||
long_url = reply.raw_text
|
||||
else:
|
||||
long_url = m_text
|
||||
await utils.answer(message, "Creating...")
|
||||
fetcher = post(f"https://clck.ru/--?url={long_url}").text
|
||||
await utils.answer(message, fetcher)
|
||||
|
||||
async def lmgtfycmd(self, message):
|
||||
"""Use in reply to another message or as .lmgtfy <text>"""
|
||||
text = utils.get_args_raw(message)
|
||||
if not text:
|
||||
if message.is_reply:
|
||||
text = (await message.get_reply_message()).message
|
||||
else:
|
||||
text = self.strings("default", message)
|
||||
query_encoded = urllib.parse.quote_plus(text)
|
||||
lmgtfy_url = "http://lmgtfy.com/?s=g&iie=1&q={}".format(query_encoded)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("result", message).format(
|
||||
utils.escape_html(lmgtfy_url), utils.escape_html(text)
|
||||
),
|
||||
)
|
||||
|
||||
async def nullcmd(self, message):
|
||||
"""Shorten the link using the nullify service"""
|
||||
chat = "@nullifybot"
|
||||
reply = await message.get_reply_message()
|
||||
async with message.client.conversation(chat) as conv:
|
||||
if not reply:
|
||||
text = utils.get_args_raw(message)
|
||||
else:
|
||||
text = await message.get_reply_message()
|
||||
try:
|
||||
response = conv.wait_event(
|
||||
events.NewMessage(incoming=True, from_users=1481485420)
|
||||
)
|
||||
mm = await message.client.send_message(chat, text)
|
||||
response = await response
|
||||
await mm.delete()
|
||||
except YouBlockedUserError:
|
||||
await message.edit("<code>Unblock @nullifybot</code>")
|
||||
return
|
||||
await message.edit(response.text.replace("🔗 Твоя ссылка: ", ""))
|
||||
await message.client(
|
||||
functions.messages.DeleteHistoryRequest(
|
||||
peer="nullifybot", max_id=0, just_clear=False, revoke=True
|
||||
)
|
||||
)
|
||||
302
GeekTG/FTG-Modules/video_editor.py
Normal file
302
GeekTG/FTG-Modules/video_editor.py
Normal file
@@ -0,0 +1,302 @@
|
||||
"""
|
||||
.------.------.------.------.------.------.------.------.------.------.
|
||||
|D.--. |4.--. |N.--. |1.--. |3.--. |L.--. |3.--. |K.--. |0.--. |0.--. |
|
||||
| :/\: | :/\: | :(): | :/\: | :(): | :/\: | :(): | :/\: | :/\: | :/\: |
|
||||
| (__) | :\/: | ()() | (__) | ()() | (__) | ()() | :\/: | :\/: | :\/: |
|
||||
| '--'D| '--'4| '--'N| '--'1| '--'3| '--'L| '--'3| '--'K| '--'0| '--'0|
|
||||
`------`------`------`------`------`------`------`------`------`------'
|
||||
|
||||
Copyright 2022 t.me/D4n13l3k00
|
||||
Licensed under the Creative Commons CC BY-NC-ND 4.0
|
||||
|
||||
Full license text can be found at:
|
||||
https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
||||
|
||||
Human-friendly one:
|
||||
https://creativecommons.org/licenses/by-nc-nd/4.0
|
||||
"""
|
||||
# meta developer: @D4n13l3k00
|
||||
|
||||
|
||||
# requires: moviepy requests
|
||||
|
||||
import os
|
||||
import random as rnd
|
||||
import re
|
||||
import string
|
||||
|
||||
import requests
|
||||
from moviepy.editor import *
|
||||
from telethon import types
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class VideoEditorMod(loader.Module):
|
||||
"Module for working with video"
|
||||
|
||||
strings = {
|
||||
"name": "VideoEditor",
|
||||
"downloading": "<b>[{}]</b> Downloading...",
|
||||
"working": "<b>[{}]</b> Working...",
|
||||
"exporting": "<b>[{}]</b> Exporting...",
|
||||
"set_value": "<b>[{}]</b> Specify the level from {} to {}...",
|
||||
"reply": "<b>[{}]</b> reply to video/gif...",
|
||||
"set_time": "<b>[{}]</b> Specify the time in the format start(ms):end(ms)",
|
||||
"set_link": "<b>[{}]</b> Enter link...",
|
||||
}
|
||||
|
||||
@loader.owner
|
||||
async def xflipvcmd(self, m: types.Message):
|
||||
""".xflipv <reply_to_video> - Flip video by X"""
|
||||
vid = await get_video(self, m, "XFlip")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.mirror_x)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def yflipvcmd(self, m: types.Message):
|
||||
""".yflipv <reply_to_video> - Flip video by Y"""
|
||||
vid = await get_video(self, m, "YFlip")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.mirror_y)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def bwvcmd(self, m: types.Message):
|
||||
""".bwv <reply_to_video> - BlackWhite"""
|
||||
vid = await get_video(self, m, "BlackWhite")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.blackwhite)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def revvcmd(self, m: types.Message):
|
||||
""".revv <reply_to_video> - Reverse video"""
|
||||
vid = await get_video(self, m, "Reverse")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.time_mirror)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def paintvcmd(self, m: types.Message):
|
||||
""".paintv <reply_to_video> - Paint effect"""
|
||||
vid = await get_video(self, m, "Paint")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.painting)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def invertvcmd(self, m: types.Message):
|
||||
""".invertv <reply_to_video> - Invert colors"""
|
||||
vid = await get_video(self, m, "Invert")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.invert_colors)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def rmsvcmd(self, m: types.Message):
|
||||
""".rmsv <reply_to_video> - Remove sound (to gif without compression)"""
|
||||
vid = await get_video(self, m, "NoAudio")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.without_audio()
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def cutvcmd(self, m: types.Message):
|
||||
""".cutv <int [Default 30]> <reply_to_video> - Cut video"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
return await utils.answer(m, self.strings("set_time", m).format("Cut"))
|
||||
r = re.compile(r"^(?P<start>\d+){0,1}:(?P<end>-?\d+){0,1}$")
|
||||
ee = r.match(args)
|
||||
if not ee:
|
||||
return await utils.answer(m, self.strings("set_time", m).format("Cut"))
|
||||
start = int(ee.group("start")) if ee.group("start") else 0
|
||||
end = int(ee.group("end")) if ee.group("end") else None
|
||||
vid = await get_video(self, m, "Cut")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.subclip(start, end)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def audvcmd(self, m: types.Message):
|
||||
""".audv <link> <reply_to_video> - Add audio to video"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
return await utils.answer(m, self.strings("set_link", m).format("Audio"))
|
||||
r = re.compile(
|
||||
r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*"
|
||||
)
|
||||
ee = r.match(args)
|
||||
if not ee:
|
||||
return await utils.answer(m, self.strings("set_link", m).format("Audio"))
|
||||
vid = await get_video(self, m, "Audio")
|
||||
if not vid:
|
||||
return
|
||||
a = requests.get(args)
|
||||
nm = "".join(rnd.sample(string.ascii_letters, 24)) + ".mp3"
|
||||
if a.status_code == 200:
|
||||
open(nm, "wb").write(a.content)
|
||||
else:
|
||||
return
|
||||
out = vid.video
|
||||
out.audio = CompositeAudioClip([AudioFileClip(nm)])
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
try:
|
||||
os.remove(nm)
|
||||
except:
|
||||
pass
|
||||
|
||||
@loader.owner
|
||||
async def fpsvcmd(self, m: types.Message):
|
||||
""".fpsv <int [Default 30]> <reply_to_video> - Change fps"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
fps = 30
|
||||
elif re.match(r"^\d+$", args) and (0 < int(args) < 241):
|
||||
fps = int(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("FPS", 1, 240)
|
||||
)
|
||||
vid = await get_video(self, m, "FPS")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.set_fps(fps)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def marginvcmd(self, m: types.Message):
|
||||
""".marginv <int [Default 5]> <reply_to_video> - Add marging"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
margin = 5
|
||||
elif re.match(r"^\d+$", args) and (0 < int(args) < 101):
|
||||
margin = int(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Scale", 1, 100)
|
||||
)
|
||||
vid = await get_video(self, m, "Margin")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.margin, margin)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def speedvcmd(self, m: types.Message):
|
||||
""".speedv <float [Default 1.5]> <reply_to_video> - Speed"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
speed = 1.5
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (0.009 < float(args) < 10.1):
|
||||
speed = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Speed", 0.01, 10.0)
|
||||
)
|
||||
vid = await get_video(self, m, "Speed")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.speedx, speed)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def contrastvcmd(self, m: types.Message):
|
||||
""".contrastv <float [Default 1.5]> <reply_to_video> - Contrast"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
contrast = 1.5
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (0.009 < float(args) < 100.1):
|
||||
contrast = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Contrast", 0.01, 100.0)
|
||||
)
|
||||
vid = await get_video(self, m, "Contrast")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.lum_contrast, contrast=contrast)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def lumvcmd(self, m: types.Message):
|
||||
""".lumv <float [Default 25]> <reply_to_video> - Lum"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
lum = 25
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (0.009 < float(args) < 100.1):
|
||||
lum = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Lum", 0.01, 100.0)
|
||||
)
|
||||
vid = await get_video(self, m, "Lum")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.fx(vfx.lum_contrast, lum=lum)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
@loader.owner
|
||||
async def scalevcmd(self, m: types.Message):
|
||||
""".scalev <float [Default 0.75]> <reply_to_video> - Scale("Resize") video"""
|
||||
args = utils.get_args_raw(m)
|
||||
if not args:
|
||||
scale = 0.75
|
||||
elif re.match(r"^\d+(\.\d+)?$", args) and (0.009 < float(args) < 100.1):
|
||||
scale = float(args)
|
||||
else:
|
||||
return await utils.answer(
|
||||
m, self.strings("set_value", m).format("Scale", 0.01, 100.0)
|
||||
)
|
||||
vid = await get_video(self, m, "Scale")
|
||||
if not vid:
|
||||
return
|
||||
out = vid.video.resize(scale)
|
||||
await go_out(self, vid.message, vid, out, vid.pref)
|
||||
|
||||
|
||||
class VideoEditorClass:
|
||||
video: VideoFileClip = None
|
||||
message = None
|
||||
pref: str = None
|
||||
reply = None
|
||||
|
||||
|
||||
async def get_video(self, m, pref: str):
|
||||
r = await m.get_reply_message()
|
||||
if r and r.file and r.file.mime_type.split("/")[0] in ["video"]:
|
||||
vid = VideoEditorClass()
|
||||
vid.pref = pref
|
||||
vid.reply = r
|
||||
vid.message = await utils.answer(m, self.strings("downloading", m).format(pref))
|
||||
vid.video = VideoFileClip(await r.download_media())
|
||||
return vid
|
||||
await utils.answer(m, self.strings("reply", m).format(pref))
|
||||
|
||||
|
||||
async def go_out(self, m, vid: VideoEditorClass, out: VideoFileClip, pref):
|
||||
m = await utils.answer(m, self.strings("exporting", m).format(pref))
|
||||
filename = "".join(rnd.sample(string.ascii_letters, 24)) + ".mp4"
|
||||
out.write_videofile(filename)
|
||||
await utils.answer(
|
||||
m, open(filename, "rb"), reply_to=vid.reply.id, supports_streaming=True
|
||||
)
|
||||
try:
|
||||
os.remove(filename)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.remove(vid.video.filename)
|
||||
except:
|
||||
pass
|
||||
174
GeekTG/FTG-Modules/vizjener.py
Normal file
174
GeekTG/FTG-Modules/vizjener.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @trololo_1
|
||||
|
||||
import logging
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class VijenerMod(loader.Module):
|
||||
"""Конвертация текста в шифр Виженеря и наоборот."""
|
||||
|
||||
strings = {"name": "Vizjener"}
|
||||
|
||||
@loader.unrestricted
|
||||
async def tovizcmd(self, message):
|
||||
""".toviz {ключ} {текст}"""
|
||||
try:
|
||||
alphabet = [
|
||||
"",
|
||||
"а",
|
||||
"б",
|
||||
"в",
|
||||
"г",
|
||||
"д",
|
||||
"е",
|
||||
"ё",
|
||||
"ж",
|
||||
"з",
|
||||
"и",
|
||||
"й",
|
||||
"к",
|
||||
"л",
|
||||
"м",
|
||||
"н",
|
||||
"о",
|
||||
"п",
|
||||
"р",
|
||||
"с",
|
||||
"т",
|
||||
"у",
|
||||
"ф",
|
||||
"х",
|
||||
"ц",
|
||||
"ч",
|
||||
"ш",
|
||||
"щ",
|
||||
"ъ",
|
||||
"ы",
|
||||
"ь",
|
||||
"э",
|
||||
"ю",
|
||||
"я",
|
||||
]
|
||||
text = utils.get_args_raw(message)
|
||||
key = str(text.split(" ")[0])
|
||||
shifr = str(text.split(" ", maxsplit=1)[1])
|
||||
key_list = []
|
||||
shifr_list = []
|
||||
for word in key.split():
|
||||
for letter in word.lower():
|
||||
key_list.append(letter)
|
||||
|
||||
for word in shifr.split():
|
||||
for letter in word.lower():
|
||||
shifr_list.append(letter)
|
||||
shifr_list.append(" ")
|
||||
|
||||
key_index = 0
|
||||
sms = ""
|
||||
for i in range(0, len(shifr_list)):
|
||||
if shifr_list[i].isalpha():
|
||||
if key_index == len(key_list):
|
||||
key_index = 0
|
||||
a = alphabet.index(shifr_list[i])
|
||||
b = alphabet.index(key_list[key_index])
|
||||
result = int(a) + int(b)
|
||||
if result >= 33:
|
||||
result = result % 33
|
||||
if result == 0:
|
||||
result = 33
|
||||
sms += alphabet[result]
|
||||
|
||||
key_index += 1
|
||||
else:
|
||||
sms += shifr_list[i]
|
||||
await message.edit(sms)
|
||||
except:
|
||||
await message.edit(
|
||||
"<strong> ERROR. Возможно вы ввели некириллические символы, либо ввели в ключ что то кроме буквенных символов. </strong>"
|
||||
)
|
||||
|
||||
@loader.unrestricted
|
||||
async def tounvizcmd(self, message):
|
||||
""".tounviz {ключ} {текст}"""
|
||||
try:
|
||||
alphabet = [
|
||||
"",
|
||||
"а",
|
||||
"б",
|
||||
"в",
|
||||
"г",
|
||||
"д",
|
||||
"е",
|
||||
"ё",
|
||||
"ж",
|
||||
"з",
|
||||
"и",
|
||||
"й",
|
||||
"к",
|
||||
"л",
|
||||
"м",
|
||||
"н",
|
||||
"о",
|
||||
"п",
|
||||
"р",
|
||||
"с",
|
||||
"т",
|
||||
"у",
|
||||
"ф",
|
||||
"х",
|
||||
"ц",
|
||||
"ч",
|
||||
"ш",
|
||||
"щ",
|
||||
"ъ",
|
||||
"ы",
|
||||
"ь",
|
||||
"э",
|
||||
"ю",
|
||||
"я",
|
||||
]
|
||||
text = utils.get_args_raw(message)
|
||||
key = str(text.split(" ")[0])
|
||||
shifr = str(text.split(" ", maxsplit=1)[1])
|
||||
key_list = []
|
||||
shifr_list = []
|
||||
for word in key.split():
|
||||
for letter in word.lower():
|
||||
key_list.append(letter)
|
||||
|
||||
for word in shifr.split():
|
||||
for letter in word.lower():
|
||||
shifr_list.append(letter)
|
||||
shifr_list.append(" ")
|
||||
|
||||
key_index = 0
|
||||
sms = ""
|
||||
for i in range(0, len(shifr_list)):
|
||||
if shifr_list[i].isalpha():
|
||||
if key_index == len(key_list):
|
||||
key_index = 0
|
||||
a = alphabet.index(shifr_list[i])
|
||||
b = alphabet.index(key_list[key_index])
|
||||
if int(b) == 33:
|
||||
result = int(a) % int(b)
|
||||
else:
|
||||
result = int(a) - int(b)
|
||||
if result < 0:
|
||||
result = result - 1
|
||||
if result == 0:
|
||||
result = 33
|
||||
sms += alphabet[result]
|
||||
key_index += 1
|
||||
else:
|
||||
sms += shifr_list[i]
|
||||
await message.edit(sms)
|
||||
except:
|
||||
await message.edit(
|
||||
"<strong> ERROR. Возможно вы ввели некириллические символы, либо ввели в ключ что то кроме буквенных символов. </strong>"
|
||||
)
|
||||
44
GeekTG/FTG-Modules/voice_recognition.py
Normal file
44
GeekTG/FTG-Modules/voice_recognition.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @dekftgmodules
|
||||
|
||||
# requires: pydub speechRecognition
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
import speech_recognition as srec
|
||||
from pydub import AudioSegment as auds
|
||||
|
||||
from .. import loader
|
||||
|
||||
|
||||
@loader.tds
|
||||
class VoiceRecognitionMod(loader.Module):
|
||||
"""Распознавание речи через Google Recognition API"""
|
||||
|
||||
strings = {"name": "VoiceRecognition", "pref": "<b>[VRC]</b> "}
|
||||
|
||||
@loader.owner
|
||||
async def recvcmd(self, m):
|
||||
""".recv <reply to voice/audio> - распознать речь"""
|
||||
reply = await m.get_reply_message()
|
||||
if reply and reply.file.mime_type.split("/")[0] == "audio":
|
||||
await m.edit(self.strings["pref"] + "Downloading...")
|
||||
source = BytesIO(await reply.download_media(bytes))
|
||||
source.name = reply.file.name
|
||||
out = BytesIO()
|
||||
out.name = "recog.wav"
|
||||
await m.edit(self.strings["pref"] + "Converting...")
|
||||
auds.from_file(source).export(out, "wav")
|
||||
out.seek(0)
|
||||
await m.edit(self.strings["pref"] + "Processing...")
|
||||
recog = srec.Recognizer()
|
||||
sample_audio = srec.AudioFile(out)
|
||||
with sample_audio as audio_file:
|
||||
audio_content = recog.record(audio_file)
|
||||
await m.edit(
|
||||
self.strings["pref"]
|
||||
+ recog.recognize_google(audio_content, language="ru-RU")
|
||||
)
|
||||
else:
|
||||
await m.edit(self.strings["pref"] + "reply to audio/voice...")
|
||||
255
GeekTG/FTG-Modules/warn.py
Normal file
255
GeekTG/FTG-Modules/warn.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd
|
||||
|
||||
from telethon.errors import UserAdminInvalidError
|
||||
from telethon.tl.functions.channels import EditBannedRequest
|
||||
from telethon.tl.types import ChatBannedRights
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WarnsMod(loader.Module):
|
||||
"""Система предупреждений."""
|
||||
|
||||
strings = {"name": "Warns"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
|
||||
async def warncmd(self, message):
|
||||
"""Выдать варн. Используй: .warn <@ или реплай>."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
chat = await message.get_chat()
|
||||
if not chat.admin_rights and not chat.creator:
|
||||
return await message.edit("<b>Я не админ здесь.</b>")
|
||||
else:
|
||||
if not chat.admin_rights.ban_users:
|
||||
return await message.edit("<b>У меня нет нужных прав.</b>")
|
||||
|
||||
warns = self.db.get("Warns", "warns", {})
|
||||
args = utils.get_args(message)
|
||||
reply = await message.get_reply_message()
|
||||
chatid = str(message.chat_id)
|
||||
reason = "Необоснованно"
|
||||
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
|
||||
if reply:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
args = utils.get_args_raw(message)
|
||||
if args:
|
||||
reason = args
|
||||
else:
|
||||
user = await message.client.get_entity(
|
||||
args[0] if not args[0].isnumeric() else int(args[0])
|
||||
)
|
||||
if args:
|
||||
if len(args) == 1:
|
||||
args = utils.get_args_raw(message)
|
||||
user = await message.client.get_entity(
|
||||
args if not args.isnumeric() else int(args)
|
||||
)
|
||||
elif len(args) >= 2:
|
||||
reason = utils.get_args_raw(message).split(" ", 1)[1]
|
||||
userid = str(user.id)
|
||||
me = await message.client.get_me()
|
||||
if me.id == user.id:
|
||||
return await message.edit("<b>Ты не можешь себе давать предупреждение!</b>")
|
||||
|
||||
if chatid not in warns:
|
||||
warns.update({chatid: {"limit": 3, "action": "ban"}})
|
||||
if userid not in warns[chatid]:
|
||||
warns[chatid].update({userid: []})
|
||||
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
|
||||
warns[chatid][userid].append(reason)
|
||||
count = len(warns[chatid][userid])
|
||||
|
||||
if count == warns[chatid]["limit"]:
|
||||
warns[chatid].pop(userid)
|
||||
self.db.set("Warns", "warns", warns)
|
||||
try:
|
||||
if warns[chatid]["action"] == "kick":
|
||||
await message.client.kick_participant(int(chatid), user.id)
|
||||
elif warns[chatid]["action"] == "ban":
|
||||
await message.client(
|
||||
EditBannedRequest(
|
||||
int(chatid),
|
||||
user.id,
|
||||
ChatBannedRights(until_date=None, view_messages=True),
|
||||
)
|
||||
)
|
||||
elif warns[chatid]["action"] == "mute":
|
||||
await message.client(
|
||||
EditBannedRequest(
|
||||
int(chatid),
|
||||
user.id,
|
||||
ChatBannedRights(until_date=True, send_messages=True),
|
||||
)
|
||||
)
|
||||
except UserAdminInvalidError:
|
||||
return await message.edit("<b>У меня нет достаточных прав.</b>")
|
||||
else:
|
||||
return await message.edit(
|
||||
f"<b>{user.first_name} получил {count}/{warns[chatid]['limit']} предупреждения, и был ограничен в чате.</b>"
|
||||
)
|
||||
self.db.set("Warns", "warns", warns)
|
||||
await message.edit(
|
||||
f"<b><a href=\"tg://user?id={user.id}\">{user.first_name}</a> получил {count}/{warns[chatid]['limit']} предупреждений.</b>"
|
||||
+ (f"\nПричина: {reason}.</b>" if reason != "Необоснованно" else "")
|
||||
)
|
||||
|
||||
async def warnslimitcmd(self, message): # sourcery skip: last-if-guard
|
||||
"""Установить лимит предупреждений. Используй: .warnslimit <кол-во:int>."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
|
||||
warns = self.db.get("Warns", "warns", {})
|
||||
args = utils.get_args_raw(message)
|
||||
chatid = str(message.chat_id)
|
||||
|
||||
if chatid not in warns:
|
||||
warns.update({chatid: {"limit": 3}})
|
||||
if not args:
|
||||
return await message.edit(
|
||||
f"<b>Лимит предупреждений в этом чате: {warns[chatid]['limit']}</b>"
|
||||
)
|
||||
|
||||
try:
|
||||
warns[chatid].update({"limit": int(args)})
|
||||
self.db.set("Warns", "warns", warns)
|
||||
return await message.edit(
|
||||
f"<b>Лимит предупреждений в этом чате был установлен на: {warns[chatid]['limit']}</b>"
|
||||
)
|
||||
except ValueError:
|
||||
return await message.edit("Значение должно быть числом.")
|
||||
|
||||
async def warnscmd(self, message):
|
||||
"""Посмотреть кол-во варнов. Используй: .warns <@ или реплай> или <list>."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
chatid = str(message.chat_id)
|
||||
warns = self.db.get("Warns", "warns", {})
|
||||
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
|
||||
if args == "list":
|
||||
users = ""
|
||||
try:
|
||||
for _ in warns[chatid]:
|
||||
if _ not in ["limit", "action"]:
|
||||
user = await message.client.get_entity(int(_))
|
||||
users += f"• <a href='tg://user?id={int(_)}'>{user.first_name}</a> <b>| [</b><code>{_}</code><b>]</b>\n"
|
||||
return await message.edit(
|
||||
f"<b>Список тех, кто получил предупреждения:\n\n{users}"
|
||||
)
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
"<b>В этом чате никто не получал предупреждения.</b>"
|
||||
)
|
||||
|
||||
try:
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
else:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
userid = str(user.id)
|
||||
except ValueError:
|
||||
return await message.edit("<b>Не удалось найти этого пользователя.</b>")
|
||||
|
||||
try:
|
||||
if userid not in warns[chatid]:
|
||||
return await message.edit(
|
||||
"<b>Этот пользователь не получал предупреждения.</b>"
|
||||
)
|
||||
|
||||
msg = "".join(
|
||||
f"<b>{count})</b> {_}\n"
|
||||
for count, _ in enumerate(warns[chatid][userid], start=1)
|
||||
)
|
||||
return await message.edit(
|
||||
f'<b>Предупреждения <a href="tg://user?id={user.id}">{user.first_name}</a>:\n\n{msg}</b>'
|
||||
)
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
f'<b>У <a href="tg://user?id={user.id}">{user.first_name}</a> нет предупреждений.</b>'
|
||||
)
|
||||
|
||||
async def swarncmd(self, message):
|
||||
"""Изменить режим ограничения. Используй: .swarn <kick/ban/mute/none>."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
args = utils.get_args_raw(message)
|
||||
chatid = str(message.chat_id)
|
||||
warns = self.db.get("Warns", "warns", {})
|
||||
|
||||
if chatid not in warns:
|
||||
warns.update({chatid: {"action": "ban"}})
|
||||
|
||||
if args:
|
||||
if args == "kick":
|
||||
warns[chatid].update({"action": "kick"})
|
||||
elif args == "ban":
|
||||
warns[chatid].update({"action": "ban"})
|
||||
elif args == "mute":
|
||||
warns[chatid].update({"action": "mute"})
|
||||
elif args == "none":
|
||||
warns[chatid].update({"action": "none"})
|
||||
else:
|
||||
return await message.edit(
|
||||
"<b>Такого режима нет в списке.\nДоступные режимы: kick/ban/mute/none.</b>"
|
||||
)
|
||||
self.db.set("AntiMention", "action", warns)
|
||||
return await message.edit(
|
||||
f"<b>Теперь при достижения лимита предупреждений будет выполняться действие: {warns[chatid]['action']}.</b>"
|
||||
)
|
||||
else:
|
||||
return await message.edit(
|
||||
f"<b>При достижения лимита предупреждений будет выполняться действие: {warns[chatid]['action']}.</b>"
|
||||
)
|
||||
|
||||
async def clearwarnscmd(self, message):
|
||||
"""Очистить все варны. Используй: .clearwarns <@ или реплай>."""
|
||||
if message.is_private:
|
||||
return await message.edit("<b>Это не чат!</b>")
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
chatid = str(message.chat_id)
|
||||
warns = self.db.get("Warns", "warns", {})
|
||||
if not args and not reply:
|
||||
return await message.edit("<b>Нет аргументов или реплая.</b>")
|
||||
|
||||
try:
|
||||
if args:
|
||||
user = await message.client.get_entity(
|
||||
int(args) if args.isnumeric() else args
|
||||
)
|
||||
else:
|
||||
user = await message.client.get_entity(reply.sender_id)
|
||||
userid = str(user.id)
|
||||
except ValueError:
|
||||
return await message.edit("<b>Не удалось найти этого пользователя.</b>")
|
||||
|
||||
try:
|
||||
warns[chatid][userid].pop()
|
||||
if len(warns[chatid][userid]) == 0:
|
||||
warns[chatid].pop(userid)
|
||||
self.db.set("Warns", "warns", warns)
|
||||
return await message.edit(
|
||||
f'<b>У <a href="tg://user?id={user.id}">{user.first_name}</a> удалено последнее предупреждение.</b>'
|
||||
)
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
f'<b>У <a href="tg://user?id={user.id}">{user.first_name}</a> нет предупреждений.</b>'
|
||||
)
|
||||
38
GeekTG/FTG-Modules/weather.py
Normal file
38
GeekTG/FTG-Modules/weather.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @govnocodules + @ftgmodulesbyfl1yd
|
||||
|
||||
import requests
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WeatherMod(loader.Module):
|
||||
"""Weather Module"""
|
||||
|
||||
strings = {"name": "Weather"}
|
||||
|
||||
async def pwcmd(self, m):
|
||||
""" "Picture of weather.\n.aw <city>"""
|
||||
args = utils.get_args_raw(m).replace(" ", "%20")
|
||||
city = requests.get(
|
||||
f"https://wttr.in/{args if args is not None else ''}.png"
|
||||
).content
|
||||
await utils.answer(m, city)
|
||||
|
||||
async def awcmd(self, m):
|
||||
"""ASCII-art of weather.\n.aw <city>"""
|
||||
city = utils.get_args_raw(m).replace(" ", "%20")
|
||||
r = requests.get(f"https://wttr.in/{city if city is not None else ''}?0?q?T")
|
||||
await utils.answer(m, f"<code>City: {r.text}</code>")
|
||||
|
||||
async def wcmd(self, m):
|
||||
""".w <city>"""
|
||||
city = utils.get_args_raw(m).replace(" ", "%20")
|
||||
if city:
|
||||
r = requests.get("https://wttr.in/" + city + "?format=%l:+%c+%t,+%w+%m")
|
||||
else:
|
||||
r = requests.get("https://wttr.in/?format=%l:+%c+%t,+%w+%m")
|
||||
|
||||
await utils.answer(m, r.text)
|
||||
100
GeekTG/FTG-Modules/welcome.py
Normal file
100
GeekTG/FTG-Modules/welcome.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Module author: @ftgmodulesbyfl1yd
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class WelcomeMod(loader.Module):
|
||||
"""Приветствие новых пользователей в чате."""
|
||||
|
||||
strings = {"name": "Welcome"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
self.client = client
|
||||
|
||||
async def welcomecmd(self, message):
|
||||
"""Включить/выключить приветствие новых пользователей в чате.
|
||||
Используй: .welcome <clearall (по желанию)>."""
|
||||
welcome = self.db.get("Welcome", "welcome", {})
|
||||
chatid = str(message.chat_id)
|
||||
args = utils.get_args_raw(message)
|
||||
if args == "clearall":
|
||||
self.db.set("Welcome", "welcome", {})
|
||||
return await message.edit(
|
||||
"<b>[Welcome Mode]</b> Все настройки модуля сброшены."
|
||||
)
|
||||
|
||||
if chatid in welcome:
|
||||
welcome.pop(chatid)
|
||||
self.db.set("Welcome", "welcome", welcome)
|
||||
return await message.edit("<b>[Welcome Mode]</b> Деактивирован!")
|
||||
|
||||
welcome.setdefault(chatid, {})
|
||||
welcome[chatid].setdefault("message", "Добро пожаловать в чат!")
|
||||
welcome[chatid].setdefault("is_reply", False)
|
||||
self.db.set("Welcome", "welcome", welcome)
|
||||
await message.edit("<b>[Welcome Mode]</b> Активирован!")
|
||||
|
||||
async def setwelcomecmd(self, message):
|
||||
"""Установить новое приветствие новых пользователей в
|
||||
чате.\nИспользуй: .setwelcome <текст (можно использовать {name}; {
|
||||
chat})>; ничего."""
|
||||
welcome = self.db.get("Welcome", "welcome", {})
|
||||
args = utils.get_args_raw(message)
|
||||
reply = await message.get_reply_message()
|
||||
chatid = str(message.chat_id)
|
||||
chat = await message.client.get_entity(int(chatid))
|
||||
try:
|
||||
if not args and not reply:
|
||||
return await message.edit(
|
||||
f"<b>Приветствие новых "
|
||||
f"пользователей в "
|
||||
f'"{chat.title}":</b>\n\n'
|
||||
f"<b>Статус:</b> Включено.\n"
|
||||
f'<b>Приветствие:</b> {welcome[chatid]["message"]}\n\n '
|
||||
f"<b>~ Установить новое приветствие "
|
||||
f"можно с помощью команды:</b> "
|
||||
f".setwelcome <текст>."
|
||||
)
|
||||
else:
|
||||
if reply:
|
||||
welcome[chatid]["message"] = reply.id
|
||||
welcome[chatid]["is_reply"] = True
|
||||
else:
|
||||
welcome[chatid]["message"] = args
|
||||
welcome[chatid]["is_reply"] = False
|
||||
self.db.set("Welcome", "welcome", welcome)
|
||||
return await message.edit(
|
||||
"<b>Новое приветствие установлено " "успешно!</b>"
|
||||
)
|
||||
except KeyError:
|
||||
return await message.edit(
|
||||
f'<b>Приветствие новых пользователей в "{chat.title}":</b>\n\n '
|
||||
f"<b>Статус:</b> Отключено"
|
||||
)
|
||||
|
||||
async def watcher(self, message):
|
||||
"""Интересно, почему он именно watcher называется... 🤔"""
|
||||
try:
|
||||
welcome = self.db.get("Welcome", "welcome", {})
|
||||
chatid = str(message.chat_id)
|
||||
if chatid not in welcome:
|
||||
return
|
||||
if message.user_joined or message.user_added:
|
||||
user = await message.get_user()
|
||||
chat = await message.get_chat()
|
||||
if not welcome[chatid]["is_reply"]:
|
||||
return await message.reply(
|
||||
(welcome[chatid]["message"]).format(
|
||||
name=user.first_name, chat=chat.title
|
||||
)
|
||||
)
|
||||
msg = await self.client.get_messages(
|
||||
int(chatid), ids=welcome[chatid]["message"]
|
||||
)
|
||||
await message.reply(msg)
|
||||
except:
|
||||
pass
|
||||
147
GeekTG/FTG-Modules/ytdl.py
Normal file
147
GeekTG/FTG-Modules/ytdl.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# requires: youtube-dl
|
||||
|
||||
import os
|
||||
|
||||
from telethon.tl.types import DocumentAttributeAudio
|
||||
from youtube_dl import YoutubeDL
|
||||
from youtube_dl.utils import (
|
||||
ContentTooShortError,
|
||||
DownloadError,
|
||||
ExtractorError,
|
||||
GeoRestrictedError,
|
||||
MaxDownloadsReached,
|
||||
PostProcessingError,
|
||||
UnavailableVideoError,
|
||||
XAttrMetadataError,
|
||||
)
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
|
||||
@loader.tds
|
||||
class YtDlMod(loader.Module):
|
||||
"""Youtube-Dl Module"""
|
||||
|
||||
strings = {
|
||||
"name": "Youtube-Dl",
|
||||
"preparing": "<b>[YouTube-Dl]</b> Preparing...",
|
||||
"downloading": "<b>[YouTube-Dl]</b> Downloading...",
|
||||
"working": "<b>[YouTube-Dl]</b> Working...",
|
||||
"exporting": "<b>[YouTube-Dl]</b> Exporting...",
|
||||
"reply": "<b>[YouTube-Dl]</b> No link!",
|
||||
"noargs": "<b>[YouTube-Dl]</b> No args!",
|
||||
"content_too_short": "<b>[YouTube-Dl]</b> Downloading content too short!",
|
||||
"geoban": "<b>[YouTube-Dl]</b> The video is not available for your geographical location due to geographical restrictions set by the website!",
|
||||
"maxdlserr": '<b>[YouTube-Dl]</b> The download limit is as follows: " oh ahah"',
|
||||
"pperr": "<b>[YouTube-Dl]</b> Error in post-processing!",
|
||||
"noformat": "<b>[YouTube-Dl]</b> Media is not available in the requested format",
|
||||
"xameerr": "<b>[YouTube-Dl]</b> {0.code}: {0.msg}\n{0.reason}",
|
||||
"exporterr": "<b>[YouTube-Dl]</b> Error when exporting video",
|
||||
"err": "<b>[YouTube-Dl]</b> {}",
|
||||
"err2": "<b>[YouTube-Dl]</b> {}: {}",
|
||||
}
|
||||
|
||||
async def ripvcmd(self, m):
|
||||
""".ripv <link / reply_to_link> - download video"""
|
||||
await riper(self, m, "video")
|
||||
|
||||
async def ripacmd(self, m):
|
||||
""".ripa <link / reply_to_link> - download audio"""
|
||||
await riper(self, m, "audio")
|
||||
|
||||
|
||||
async def riper(self, m, type):
|
||||
reply = await m.get_reply_message()
|
||||
args = utils.get_args_raw(m)
|
||||
url = args or reply.raw_text
|
||||
if not url:
|
||||
return await utils.answer(m, self.strings("noargs", m))
|
||||
m = await utils.answer(m, self.strings("preparing", m))
|
||||
if type == "audio":
|
||||
opts = {
|
||||
"format": "bestaudio",
|
||||
"addmetadata": True,
|
||||
"key": "FFmpegMetadata",
|
||||
"writethumbnail": True,
|
||||
"prefer_ffmpeg": True,
|
||||
"geo_bypass": True,
|
||||
"nocheckcertificate": True,
|
||||
"postprocessors": [
|
||||
{
|
||||
"key": "FFmpegExtractAudio",
|
||||
"preferredcodec": "mp3",
|
||||
"preferredquality": "320",
|
||||
}
|
||||
],
|
||||
"outtmpl": "%(id)s.mp3",
|
||||
"quiet": True,
|
||||
"logtostderr": False,
|
||||
}
|
||||
video = False
|
||||
song = True
|
||||
elif type == "video":
|
||||
opts = {
|
||||
"format": "best",
|
||||
"addmetadata": True,
|
||||
"key": "FFmpegMetadata",
|
||||
"prefer_ffmpeg": True,
|
||||
"geo_bypass": True,
|
||||
"nocheckcertificate": True,
|
||||
"postprocessors": [
|
||||
{"key": "FFmpegVideoConvertor", "preferedformat": "mp4"}
|
||||
],
|
||||
"outtmpl": "%(id)s.mp4",
|
||||
"logtostderr": False,
|
||||
"quiet": True,
|
||||
}
|
||||
song = False
|
||||
video = True
|
||||
try:
|
||||
await utils.answer(m, self.strings("downloading", m))
|
||||
with YoutubeDL(opts) as rip:
|
||||
rip_data = rip.extract_info(url)
|
||||
except DownloadError as DE:
|
||||
return await utils.answer(m, self.strings("err", m).format(str(DE)))
|
||||
except ContentTooShortError:
|
||||
return await utils.answer(m, self.strings("content_too_short", m))
|
||||
except GeoRestrictedError:
|
||||
return await utils.answer(m, self.strings("geoban", m))
|
||||
except MaxDownloadsReached:
|
||||
return await utils.answer(m, self.strings("maxdlserr", m))
|
||||
except PostProcessingError:
|
||||
return await utils.answer(m, self.strings("pperr", m))
|
||||
except UnavailableVideoError:
|
||||
return await utils.answer(m, self.strings("noformat", m))
|
||||
except XAttrMetadataError as XAME:
|
||||
return await utils.answer(m, self.strings("xameerr", m).format(XAME))
|
||||
except ExtractorError:
|
||||
return await utils.answer(m, self.strings("exporterr", m))
|
||||
except Exception as e:
|
||||
return await utils.answer(
|
||||
m, self.strings("err2", m).format(str(type(e)), str(e))
|
||||
)
|
||||
if song:
|
||||
u = rip_data["uploader"] if "uploader" in rip_data else "Northing"
|
||||
await utils.answer(
|
||||
m,
|
||||
open(f"{rip_data['id']}.mp3", "rb"),
|
||||
supports_streaming=True,
|
||||
reply_to=reply.id if reply else None,
|
||||
attributes=[
|
||||
DocumentAttributeAudio(
|
||||
duration=int(rip_data["duration"]),
|
||||
title=str(rip_data["title"]),
|
||||
performer=u,
|
||||
)
|
||||
],
|
||||
)
|
||||
os.remove(f"{rip_data['id']}.mp3")
|
||||
elif video:
|
||||
await utils.answer(
|
||||
m,
|
||||
open(f"{rip_data['id']}.mp4", "rb"),
|
||||
reply_to=reply.id if reply else None,
|
||||
supports_streaming=True,
|
||||
caption=rip_data["title"],
|
||||
)
|
||||
os.remove(f"{rip_data['id']}.mp4")
|
||||
Reference in New Issue
Block a user