Commited backup

This commit is contained in:
2025-07-10 21:02:34 +03:00
parent 952c1001e3
commit da0b80823e
1310 changed files with 254133 additions and 41 deletions

View 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
View File

@@ -0,0 +1,3 @@
.idea/
.vscode/
.ruff_cache/

View File

@@ -0,0 +1,3 @@
# Friendly-Telegram Modules
## FTG Modules for [GeekTG/Friendly-Telegram](https://github.com/GeekTG/Friendly-Telegram)

View 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

View 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",
)
],
)

View 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))

View 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

View 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

View 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)

View 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
View 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()

View 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)))

View 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
View 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
#######################

View 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

View 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()

View 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()

View 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

View 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)

View 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

View 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,
)

View 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

View 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

View 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)
),
)

View 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))

View File

@@ -0,0 +1,6 @@
information
notes
purge
quotes
terminal
translate

View File

@@ -0,0 +1,6 @@
information
notes
purge
quotes
terminal
translate

182
GeekTG/FTG-Modules/morze.py Normal file
View 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)

View 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

View File

View 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
View 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

View 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(),
)
]
)
),
}

View 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

View 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]
)

View 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

View 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
View 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)

View 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)

View 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
View 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

View 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()

View 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)

View 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)

View 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

View 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]

View 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)

View 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],
)

View 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
View 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
View 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
)
)

View 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

View 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>"
)

View 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
View 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>'
)

View 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)

View 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
View 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")