fix: a bug

This commit is contained in:
xtaodada 2023-09-11 21:05:50 +08:00
parent 4ff489cf95
commit 060aa7b8fa
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
14 changed files with 170 additions and 109 deletions

View File

@ -24,15 +24,6 @@ ADMIN_MSG = """管理员邀请,自动放行。"""
async def invite(client: Client, chat_member_updated: ChatMemberUpdated): async def invite(client: Client, chat_member_updated: ChatMemberUpdated):
chat = chat_member_updated.chat chat = chat_member_updated.chat
old_chat_member = chat_member_updated.old_chat_member old_chat_member = chat_member_updated.old_chat_member
if user := chat_member_updated.new_chat_member and not old_chat_member:
if not user.user:
return
if user.user.is_self:
with contextlib.suppress(Exception):
await log(chat, user.invited_by, "NEW_GROUP")
if chat.username:
with contextlib.suppress(Exception):
await client.send_message(chat.id, MSG_PUBLIC)
if await cache.get(f"cid:{chat.id}"): if await cache.get(f"cid:{chat.id}"):
return return
member = chat_member_updated.new_chat_member member = chat_member_updated.new_chat_member
@ -43,20 +34,38 @@ async def invite(client: Client, chat_member_updated: ChatMemberUpdated):
return return
user = member.user user = member.user
old_user = old_member.user if old_member else None old_user = old_member.user if old_member else None
if user.is_self or user.is_verified or user.is_bot or user.is_deleted or user.is_support: if user.is_verified or user.is_bot or user.is_deleted or user.is_support:
return return
if member.status not in {ChatMemberStatus.MEMBER}: if member.status not in {ChatMemberStatus.MEMBER}:
return return
if old_user and old_user.id == user.id and old_user.status in { if (
old_user
and old_user.id == user.id
and old_user.status
in {
ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.ADMINISTRATOR,
ChatMemberStatus.OWNER, ChatMemberStatus.OWNER,
ChatMemberStatus.MEMBER, ChatMemberStatus.MEMBER,
ChatMemberStatus.RESTRICTED, ChatMemberStatus.RESTRICTED,
}: }
):
return return
if user and chat_member_updated.from_user and ( if user.is_self:
await bot.get_chat_member(chat.id, chat_member_updated.from_user.id) with contextlib.suppress(Exception):
).status in {ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER}: await log(chat, chat_member_updated.from_user, "NEW_GROUP")
if chat.username:
with contextlib.suppress(Exception):
await client.send_message(chat.id, MSG_PUBLIC)
return
from_user = chat_member_updated.from_user
if from_user and from_user.id == user.id:
from_user = None
if (
user
and from_user
and (await bot.get_chat_member(chat.id, from_user.id)).status
in {ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER}
):
try: try:
msg = await client.send_message(chat.id, ADMIN_MSG) msg = await client.send_message(chat.id, ADMIN_MSG)
except Exception: except Exception:
@ -75,7 +84,9 @@ async def invite(client: Client, chat_member_updated: ChatMemberUpdated):
await msg.delete() await msg.delete()
if not msg_.sticker: if not msg_.sticker:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await bot.ban_chat_member(chat.id, user.id, datetime.now() + timedelta(minutes=5)) await bot.ban_chat_member(
chat.id, user.id, datetime.now() + timedelta(minutes=5)
)
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await log(chat, user, "FAIL_ERROR") await log(chat, user, "FAIL_ERROR")
else: else:
@ -87,6 +98,8 @@ async def invite(client: Client, chat_member_updated: ChatMemberUpdated):
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await msg.delete() await msg.delete()
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await bot.ban_chat_member(chat.id, user.id, datetime.now() + timedelta(minutes=5)) await bot.ban_chat_member(
chat.id, user.id, datetime.now() + timedelta(minutes=5)
)
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await log(chat, user, "FAIL_TIMEOUT") await log(chat, user, "FAIL_TIMEOUT")

View File

@ -31,7 +31,13 @@ async def re_verify(client: Client, message: Message):
return return
user = message.reply_to_message.from_user user = message.reply_to_message.from_user
if user.is_self or user.is_verified or user.is_bot or user.is_deleted or user.is_support: if (
user.is_self
or user.is_verified
or user.is_bot
or user.is_deleted
or user.is_support
):
return return
member = await client.get_chat_member(chat.id, user.id) member = await client.get_chat_member(chat.id, user.id)
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
@ -47,9 +53,14 @@ async def re_verify(client: Client, message: Message):
if not msg_.sticker: if not msg_.sticker:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await message.reply_to_message.delete() await message.reply_to_message.delete()
if member.status not in [ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR]: if member.status not in [
ChatMemberStatus.OWNER,
ChatMemberStatus.ADMINISTRATOR,
]:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await bot.ban_chat_member(chat.id, user.id, datetime.now() + timedelta(minutes=5)) await bot.ban_chat_member(
chat.id, user.id, datetime.now() + timedelta(minutes=5)
)
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await log(chat, user, "FAIL_ERROR") await log(chat, user, "FAIL_ERROR")
else: else:
@ -62,8 +73,13 @@ async def re_verify(client: Client, message: Message):
await msg.delete() await msg.delete()
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await message.reply_to_message.delete() await message.reply_to_message.delete()
if member.status not in [ChatMemberStatus.OWNER, ChatMemberStatus.ADMINISTRATOR]: if member.status not in [
ChatMemberStatus.OWNER,
ChatMemberStatus.ADMINISTRATOR,
]:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await bot.ban_chat_member(chat.id, user.id, datetime.now() + timedelta(minutes=5)) await bot.ban_chat_member(
chat.id, user.id, datetime.now() + timedelta(minutes=5)
)
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
await log(chat, user, "FAIL_TIMEOUT") await log(chat, user, "FAIL_TIMEOUT")

View File

@ -15,10 +15,17 @@ async def start(client: Client, message: Message):
quote=True, quote=True,
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
[ [
[InlineKeyboardButton( [
"Github", InlineKeyboardButton(
url="https://github.com/Xtao-Labs/sticker-captcha-bot")], "Github", url="https://github.com/Xtao-Labs/sticker-captcha-bot"
[InlineKeyboardButton( )
],
[
InlineKeyboardButton(
"邀请入群", "邀请入群",
url=f"https://t.me/{me.username}?startgroup=start&admin=can_invite_users")] url=f"https://t.me/{me.username}?startgroup=start&admin=can_invite_users",
])) )
],
]
),
)

View File

@ -22,7 +22,7 @@ import pyrogram
def dice(ctx, message): def dice(ctx, message):
return hasattr(message, 'dice') and message.dice return hasattr(message, "dice") and message.dice
pyrogram.filters.dice = dice pyrogram.filters.dice = dice

View File

@ -1,4 +1,10 @@
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ForceReply from pyrogram.types import (
InlineKeyboardButton,
InlineKeyboardMarkup,
KeyboardButton,
ReplyKeyboardMarkup,
ForceReply,
)
def ikb(rows=None): def ikb(rows=None):
@ -15,7 +21,7 @@ def ikb(rows=None):
# return {'inline_keyboard': lines} # return {'inline_keyboard': lines}
def btn(text, value, type='callback_data'): def btn(text, value, type="callback_data"):
return InlineKeyboardButton(text, **{type: value}) return InlineKeyboardButton(text, **{type: value})
# return {'text': text, type: value} # return {'text': text, type: value}
@ -34,13 +40,18 @@ def bki(keyboard):
def ntb(button): def ntb(button):
for btn_type in ['callback_data', 'url', 'switch_inline_query', 'switch_inline_query_current_chat', for btn_type in [
'callback_game']: "callback_data",
"url",
"switch_inline_query",
"switch_inline_query_current_chat",
"callback_game",
]:
value = getattr(button, btn_type) value = getattr(button, btn_type)
if value: if value:
break break
button = [button.text, value] button = [button.text, value]
if btn_type != 'callback_data': if btn_type != "callback_data":
button.append(btn_type) button.append(btn_type)
return button return button
# return {'text': text, type: value} # return {'text': text, type: value}
@ -72,4 +83,4 @@ def force_reply(selective=True):
def array_chunk(input_, size): def array_chunk(input_, size):
return [input_[i:i + size] for i in range(0, len(input_), size)] return [input_[i : i + size] for i in range(0, len(input_), size)]

View File

@ -47,12 +47,8 @@ class Client:
chat_id = chat.id chat_id = chat.id
future = self.loop.create_future() future = self.loop.create_future()
future.add_done_callback( future.add_done_callback(functools.partial(self.clear_listener, chat_id))
functools.partial(self.clear_listener, chat_id) self.listening.update({chat_id: {"future": future, "filters": filters}})
)
self.listening.update({
chat_id: {"future": future, "filters": filters}
})
try: try:
return await asyncio.wait_for(future, timeout) return await asyncio.wait_for(future, timeout)
except asyncio.exceptions.TimeoutError as e: except asyncio.exceptions.TimeoutError as e:
@ -73,11 +69,11 @@ class Client:
@patchable @patchable
def cancel_listener(self, chat_id): def cancel_listener(self, chat_id):
listener = self.listening.get(chat_id) listener = self.listening.get(chat_id)
if not listener or listener['future'].done(): if not listener or listener["future"].done():
return return
listener['future'].set_exception(ListenerCanceled()) listener["future"].set_exception(ListenerCanceled())
self.clear_listener(chat_id, listener['future']) self.clear_listener(chat_id, listener["future"])
@patchable @patchable
def cancel_all_listener(self): def cancel_all_listener(self):
@ -95,26 +91,26 @@ class MessageHandler:
@patchable @patchable
async def resolve_listener(self, client, message, *args): async def resolve_listener(self, client, message, *args):
listener = client.listening.get(message.chat.id) listener = client.listening.get(message.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
listener['future'].set_result(message) listener["future"].set_result(message)
else: else:
if listener and listener['future'].done(): if listener and listener["future"].done():
client.clear_listener(message.chat.id, listener['future']) client.clear_listener(message.chat.id, listener["future"])
await self.user_callback(client, message, *args) await self.user_callback(client, message, *args)
@patchable @patchable
async def check(self, client, update): async def check(self, client, update):
listener = client.listening.get(update.chat.id) listener = client.listening.get(update.chat.id)
if listener and not listener['future'].done(): if listener and not listener["future"].done():
return await listener['filters'](client, update) if callable(listener['filters']) else True
return ( return (
await self.filters(client, update) await listener["filters"](client, update)
if callable(self.filters) if callable(listener["filters"])
else True else True
) )
return await self.filters(client, update) if callable(self.filters) else True
@patch(pyrogram.types.user_and_chats.chat.Chat) @patch(pyrogram.types.user_and_chats.chat.Chat)
class Chat(pyrogram.types.Chat): class Chat(pyrogram.types.Chat):
@ -152,9 +148,7 @@ class Message(pyrogram.types.Message):
async def safe_delete(self, revoke: bool = True): async def safe_delete(self, revoke: bool = True):
try: try:
return await self._client.delete_messages( return await self._client.delete_messages(
chat_id=self.chat.id, chat_id=self.chat.id, message_ids=self.id, revoke=revoke
message_ids=self.id,
revoke=revoke
) )
except Exception as e: # noqa except Exception as e: # noqa
return False return False

View File

@ -23,8 +23,8 @@ from ..helpers import array_chunk
class Pagination: class Pagination:
def __init__(self, objects, page_data=None, item_data=None, item_title=None): def __init__(self, objects, page_data=None, item_data=None, item_title=None):
default_page_callback = (lambda x: str(x)) default_page_callback = lambda x: str(x)
default_item_callback = (lambda i, pg: f'[{pg}] {i}') default_item_callback = lambda i, pg: f"[{pg}] {i}"
self.objects = objects self.objects = objects
self.page_data = page_data or default_page_callback self.page_data = page_data or default_page_callback
self.item_data = item_data or default_item_callback self.item_data = item_data or default_item_callback
@ -38,7 +38,9 @@ class Pagination:
cutted = self.objects[offset:stop] cutted = self.objects[offset:stop]
total = len(self.objects) total = len(self.objects)
pages_range = [*range(1, math.ceil(total / quant_per_page) + 1)] # each item is a page pages_range = [
*range(1, math.ceil(total / quant_per_page) + 1)
] # each item is a page
last_page = len(pages_range) last_page = len(pages_range)
nav = [] nav = []
@ -49,19 +51,20 @@ class Pagination:
text = f"· {n} ·" if n == page else n text = f"· {n} ·" if n == page else n
nav.append((text, self.page_data(n))) nav.append((text, self.page_data(n)))
if last_page >= 4: if last_page >= 4:
nav.append( nav.append(("4 " if last_page > 5 else 4, self.page_data(4)))
('4 ' if last_page > 5 else 4, self.page_data(4))
)
if last_page > 4: if last_page > 4:
nav.append( nav.append(
(f'{last_page} »' if last_page > 5 else last_page, self.page_data(last_page)) (
f"{last_page} »" if last_page > 5 else last_page,
self.page_data(last_page),
)
) )
elif page >= last_page - 2: elif page >= last_page - 2:
nav.extend( nav.extend(
[ [
('« 1' if last_page > 5 else 1, self.page_data(1)), ("« 1" if last_page > 5 else 1, self.page_data(1)),
( (
f' {last_page - 3}' if last_page > 5 else last_page - 3, f" {last_page - 3}" if last_page > 5 else last_page - 3,
self.page_data(last_page - 3), self.page_data(last_page - 3),
), ),
] ]
@ -72,16 +75,15 @@ class Pagination:
nav.append((text, self.page_data(n))) nav.append((text, self.page_data(n)))
else: else:
nav = [ nav = [
('« 1', self.page_data(1)), ("« 1", self.page_data(1)),
(f' {page - 1}', self.page_data(page - 1)), (f" {page - 1}", self.page_data(page - 1)),
(f'· {page} ·', "noop"), (f"· {page} ·", "noop"),
(f'{page + 1} ', self.page_data(page + 1)), (f"{page + 1} ", self.page_data(page + 1)),
(f'{last_page} »', self.page_data(last_page)), (f"{last_page} »", self.page_data(last_page)),
] ]
buttons = [ buttons = [
(self.item_title(item, page), self.item_data(item, page)) (self.item_title(item, page), self.item_data(item, page)) for item in cutted
for item in cutted
] ]
kb_lines = array_chunk(buttons, columns) kb_lines = array_chunk(buttons, columns)

View File

@ -2,10 +2,9 @@ class TimeoutConversationError(Exception):
""" """
Occurs when the conversation times out. Occurs when the conversation times out.
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__("Response read timed out")
"Response read timed out"
)
class ListenerCanceled(Exception): class ListenerCanceled(Exception):
@ -14,6 +13,4 @@ class ListenerCanceled(Exception):
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__("Listener was canceled")
"Listener was canceled"
)

View File

@ -21,12 +21,12 @@ along with pyromod. If not, see <https://www.gnu.org/licenses/>.
def patch(obj): def patch(obj):
def is_patchable(item): def is_patchable(item):
return getattr(item[1], 'patchable', False) return getattr(item[1], "patchable", False)
def wrapper(container): def wrapper(container):
for name, func in filter(is_patchable, container.__dict__.items()): for name, func in filter(is_patchable, container.__dict__.items()):
old = getattr(obj, name, None) old = getattr(obj, name, None)
setattr(obj, f'old{name}', old) setattr(obj, f"old{name}", old)
setattr(obj, name, func) setattr(obj, name, func)
return container return container

View File

@ -37,26 +37,34 @@ start_time = datetime.now(timezone.utc)
with contextlib.suppress(ImportError): with contextlib.suppress(ImportError):
import uvloop # noqa import uvloop # noqa
uvloop.install() uvloop.install()
if not scheduler.running: if not scheduler.running:
scheduler.start() scheduler.start()
bot = Client("sticker", bot = Client(
"sticker",
bot_token=Config.BOT_TOKEN, bot_token=Config.BOT_TOKEN,
session_string=Config.STRING_SESSION, session_string=Config.STRING_SESSION,
api_id=Config.API_ID, api_id=Config.API_ID,
api_hash=Config.API_HASH, api_hash=Config.API_HASH,
ipv6=Config.IPV6, ipv6=Config.IPV6,
proxy=Config.PROXY, proxy=Config.PROXY,
plugins={"root": "plugins"}) plugins={"root": "plugins"},
)
async def log(chat, user, action): async def log(chat, user, action):
if not Config.LOG_CHANNEL: if not Config.LOG_CHANNEL:
return return
me = await bot.get_me() me = await bot.get_me()
event = {"FAIL_ERROR": "回答错误", "FAIL_TIMEOUT": "回答超时", "ACCEPT": "通过验证", "NEW_GROUP": "加入群组", event = {
"REQUEST": "发起验证"} "FAIL_ERROR": "回答错误",
"FAIL_TIMEOUT": "回答超时",
"ACCEPT": "通过验证",
"NEW_GROUP": "加入群组",
"REQUEST": "发起验证",
}
msg = """#%s msg = """#%s
群组: %s 群组: %s
群组id: <code>%s</code> 群组id: <code>%s</code>

View File

@ -9,4 +9,5 @@ async def main():
await idle() await idle()
await bot.stop() await bot.stop()
bot.run(main()) bot.run(main())

View File

@ -13,9 +13,9 @@ def strtobool(val):
'val' is anything else. 'val' is anything else.
""" """
val = val.lower() val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'): if val in ("y", "yes", "t", "true", "on", "1"):
return 1 return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'): elif val in ("n", "no", "f", "false", "off", "0"):
return 0 return 0
else: else:
raise ValueError("invalid truth value %r" % (val,)) raise ValueError("invalid truth value %r" % (val,))
@ -24,7 +24,9 @@ def strtobool(val):
try: try:
config = load(open(r"config.yml"), Loader=FullLoader) config = load(open(r"config.yml"), Loader=FullLoader)
except FileNotFoundError: except FileNotFoundError:
print("The configuration file does not exist, and a new configuration file is being generated.") print(
"The configuration file does not exist, and a new configuration file is being generated."
)
copyfile(f"{os.getcwd()}{os.sep}config.gen.yml", "config.yml") copyfile(f"{os.getcwd()}{os.sep}config.gen.yml", "config.yml")
sys.exit(1) sys.exit(1)

View File

@ -26,24 +26,32 @@ async def decline_request(chat_join_request: ChatJoinRequest):
def add_delete_message_job(message: Message, delete_seconds: int = 60): def add_delete_message_job(message: Message, delete_seconds: int = 60):
scheduler.add_job( scheduler.add_job(
delete_message, "date", delete_message,
"date",
id=f"{message.chat.id}|{message.id}|delete_message", id=f"{message.chat.id}|{message.id}|delete_message",
name=f"{message.chat.id}|{message.id}|delete_message", name=f"{message.chat.id}|{message.id}|delete_message",
args=[message], args=[message],
run_date=datetime.datetime.now(pytz.timezone("Asia/Shanghai")) + datetime.timedelta(seconds=delete_seconds), run_date=datetime.datetime.now(pytz.timezone("Asia/Shanghai"))
replace_existing=True) + datetime.timedelta(seconds=delete_seconds),
replace_existing=True,
)
def add_decline_request_job(chat_join_request: ChatJoinRequest): def add_decline_request_job(chat_join_request: ChatJoinRequest):
scheduler.add_job( scheduler.add_job(
decline_request, "date", decline_request,
"date",
id=f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request", id=f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request",
name=f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request", name=f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request",
args=[chat_join_request], args=[chat_join_request],
run_date=datetime.datetime.now(pytz.timezone("Asia/Shanghai")) + datetime.timedelta(seconds=60), run_date=datetime.datetime.now(pytz.timezone("Asia/Shanghai"))
replace_existing=True) + datetime.timedelta(seconds=60),
replace_existing=True,
)
def rem_decline_request_job(chat_join_request: ChatJoinRequest): def rem_decline_request_job(chat_join_request: ChatJoinRequest):
if job := scheduler.get_job(f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request"): if job := scheduler.get_job(
f"{chat_join_request.chat.id}|{chat_join_request.from_user.id}|decline_request"
):
job.remove() job.remove()

View File

@ -25,11 +25,13 @@ class Client(Client): # noqa
async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]: async def listen(self, chat_id, filters=None, timeout=None) -> Optional[Message]:
return return
async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs) -> Optional[Message]: async def ask(
self, chat_id, text, filters=None, timeout=None, *args, **kwargs
) -> Optional[Message]:
return return
def cancel_listener(self, chat_id): def cancel_listener(self, chat_id):
""" Cancel the conversation with the given chat_id. """ """Cancel the conversation with the given chat_id."""
return return