🔖 Update to v1.2.4

This commit is contained in:
xtaodada 2022-08-02 00:04:45 +08:00
parent e1660d0dec
commit 21f58ba3d4
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
9 changed files with 201 additions and 65 deletions

View File

@ -14,7 +14,7 @@ import pyromod.listen
from pyrogram import Client from pyrogram import Client
import sys import sys
pgm_version = "1.2.3" pgm_version = "1.2.4"
CMD_LIST = {} CMD_LIST = {}
module_dir = __path__[0] module_dir = __path__[0]
working_dir = getcwd() working_dir = getcwd()

View File

@ -7,6 +7,7 @@ from pyrogram import idle
from pagermaid import bot, logs, working_dir from pagermaid import bot, logs, working_dir
from pagermaid.hook import Hook from pagermaid.hook import Hook
from pagermaid.modules import module_list, plugin_list from pagermaid.modules import module_list, plugin_list
from pagermaid.single_utils import safe_remove
from pagermaid.utils import lang, process_exit from pagermaid.utils import lang, process_exit
path.insert(1, f"{working_dir}{sep}plugins") path.insert(1, f"{working_dir}{sep}plugins")
@ -18,6 +19,9 @@ async def main():
await bot.start() await bot.start()
me = await bot.get_me() me = await bot.get_me()
if me.is_bot:
safe_remove("pagermaid.session")
exit()
logs.info(f"{lang('save_id')} {me.first_name}({me.id})") logs.info(f"{lang('save_id')} {me.first_name}({me.id})")
for module_name in module_list: for module_name in module_list:

View File

@ -5,7 +5,7 @@ from yaml import load, FullLoader, safe_load
from shutil import copyfile from shutil import copyfile
def strtobool(val): def strtobool(val, default=False):
"""Convert a string representation of truth to true (1) or false (0). """Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
@ -18,7 +18,8 @@ def strtobool(val):
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,)) print("[Degrade] invalid truth value %r" % (val,))
return default
try: try:
@ -35,17 +36,20 @@ class Config:
API_HASH = os.environ.get("API_HASH", config["api_hash"]) API_HASH = os.environ.get("API_HASH", config["api_hash"])
STRING_SESSION = os.environ.get("STRING_SESSION") STRING_SESSION = os.environ.get("STRING_SESSION")
DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"])) DEBUG = strtobool(os.environ.get("PGM_DEBUG", config["debug"]))
ERROR_REPORT = strtobool(os.environ.get("PGM_ERROR_REPORT", config["error_report"])) ERROR_REPORT = strtobool(os.environ.get("PGM_ERROR_REPORT", config["error_report"]), True)
LANGUAGE = os.environ.get("PGM_LANGUAGE", config["application_language"]) LANGUAGE = os.environ.get("PGM_LANGUAGE", config["application_language"])
REGION = os.environ.get("PGM_REGION", config["application_region"]) REGION = os.environ.get("PGM_REGION", config["application_region"])
TTS = os.environ.get("PGM_TTS", config["application_tts"]) TTS = os.environ.get("PGM_TTS", config["application_tts"])
LOG = strtobool(os.environ.get("PGM_LOG", config["log"])) LOG = strtobool(os.environ.get("PGM_LOG", config["log"]))
LOG_ID = int(os.environ.get("PGM_LOG_ID", config["log_chatid"])) LOG_ID = int(os.environ.get("PGM_LOG_ID", config["log_chatid"]))
IPV6 = strtobool(os.environ.get("PGM_IPV6", config["ipv6"])) IPV6 = strtobool(os.environ.get("PGM_IPV6", config["ipv6"]))
ALLOW_ANALYTIC = strtobool(os.environ.get("PGM_ALLOW_ANALYTIC", config["allow_analytic"]), True)
SENTRY_API = "https://0785960e63e04279a694d0486d47d9ea@o1342815.ingest.sentry.io/6617119"
MIXPANEL_API = "c79162511383b0fa1e9c062a2a86c855"
TIME_FORM = os.environ.get("PGM_TIME_FORM", config["time_form"]) TIME_FORM = os.environ.get("PGM_TIME_FORM", config["time_form"])
DATE_FORM = os.environ.get("PGM_DATE_FORM", config["date_form"]) DATE_FORM = os.environ.get("PGM_DATE_FORM", config["date_form"])
START_FORM = os.environ.get("PGM_START_FORM", config["start_form"]) START_FORM = os.environ.get("PGM_START_FORM", config["start_form"])
SILENT = strtobool(os.environ.get("PGM_PGM_SILENT", config["silent"])) SILENT = strtobool(os.environ.get("PGM_PGM_SILENT", config["silent"]), True)
PROXY_ADDRESS = os.environ.get("PGM_PROXY_ADDRESS", config["proxy_addr"]) PROXY_ADDRESS = os.environ.get("PGM_PROXY_ADDRESS", config["proxy_addr"])
PROXY_PORT = os.environ.get("PGM_PROXY_PORT", config["proxy_port"]) PROXY_PORT = os.environ.get("PGM_PROXY_PORT", config["proxy_port"])
PROXY = None PROXY = None

View File

@ -1,4 +1,5 @@
import asyncio import asyncio
import sys
from pyrogram import StopPropagation from pyrogram import StopPropagation
@ -81,31 +82,64 @@ class Hook:
logs.info(f"[shutdown]: {type(exception)}: {exception}") logs.info(f"[shutdown]: {type(exception)}: {exception}")
@staticmethod @staticmethod
async def command_pre(message: Message): async def command_pre(message: Message, command):
if cors := [pre(**inject(message, pre)) for pre in hook_functions["command_pre"]]: # noqa cors = []
try: try:
for pre in hook_functions["command_pre"]:
try:
data = inject(message, pre, command=command)
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue
cors.append(pre(**data)) # noqa
if cors:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except StopPropagation as e: except SystemExit:
raise StopPropagation from e await Hook.shutdown()
except Exception as exception: sys.exit(0)
logs.info(f"[command_pre]: {type(exception)}: {exception}") except StopPropagation as e:
raise StopPropagation from e
except Exception as exception:
logs.info(f"[command_pre]: {type(exception)}: {exception}")
@staticmethod @staticmethod
async def command_post(message: Message): async def command_post(message: Message, command):
if cors := [post(**inject(message, post)) for post in hook_functions["command_post"]]: # noqa cors = []
try: try:
for post in hook_functions["command_post"]:
try:
data = inject(message, post, command=command)
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue
cors.append(post(**data)) # noqa
if cors:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except StopPropagation as e: except SystemExit:
raise StopPropagation from e await Hook.shutdown()
except Exception as exception: sys.exit(0)
logs.info(f"[command_post]: {type(exception)}: {exception}") except StopPropagation as e:
raise StopPropagation from e
except Exception as exception:
logs.info(f"[command_post]: {type(exception)}: {exception}")
@staticmethod @staticmethod
async def process_error_exec(message: Message, exc_info: BaseException, exc_format: str): async def process_error_exec(message: Message, command, exc_info: BaseException, exc_format: str):
if cors := [error(**inject(message, error, exc_info=exc_info, exc_format=exc_format)) for error in hook_functions["process_error"]]: # noqa cors = []
try: try:
for error in hook_functions["process_error"]:
try:
data = inject(message, error, command=command, exc_info=exc_info, exc_format=exc_format)
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")
continue
cors.append(error(**data)) # noqa
if cors:
await asyncio.gather(*cors) await asyncio.gather(*cors)
except StopPropagation as e: except SystemExit:
raise StopPropagation from e await Hook.shutdown()
except Exception as exception: sys.exit(0)
logs.info(f"[process_error]: {type(exception)}: {exception}") except StopPropagation as e:
raise StopPropagation from e
except Exception as exception:
logs.info(f"[process_error]: {type(exception)}: {exception}")

View File

@ -6,6 +6,7 @@ from time import strftime, gmtime, time
from traceback import format_exc from traceback import format_exc
from pyrogram import ContinuePropagation, StopPropagation, filters, Client from pyrogram import ContinuePropagation, StopPropagation, filters, Client
from pyrogram.errors import Flood, Forbidden
from pyrogram.errors.exceptions.bad_request_400 import ( from pyrogram.errors.exceptions.bad_request_400 import (
MessageIdInvalid, MessageIdInvalid,
MessageNotModified, MessageNotModified,
@ -139,7 +140,7 @@ def listener(**args):
read_context[(message.chat.id, message.id)] = True read_context[(message.chat.id, message.id)] = True
if command: if command:
await Hook.command_pre(message) await Hook.command_pre(message, command)
if data := inject(message, function): if data := inject(message, function):
await function(**data) await function(**data)
else: else:
@ -150,12 +151,12 @@ def listener(**args):
elif function.__code__.co_argcount == 2: elif function.__code__.co_argcount == 2:
await function(client, message) await function(client, message)
if command: if command:
await Hook.command_post(message) await Hook.command_post(message, command)
except StopPropagation as e: except StopPropagation as e:
raise StopPropagation from e raise StopPropagation from e
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
raise KeyboardInterrupt from e raise KeyboardInterrupt from e
except MessageNotModified: except (UserNotParticipant, MessageNotModified, MessageEmpty, Flood, Forbidden):
pass pass
except MessageIdInvalid: except MessageIdInvalid:
logs.warning( logs.warning(
@ -179,8 +180,6 @@ def listener(**args):
) )
with contextlib.suppress(BaseException): with contextlib.suppress(BaseException):
await message.edit(lang("reload_des")) await message.edit(lang("reload_des"))
except UserNotParticipant:
pass
except ContinuePropagation as e: except ContinuePropagation as e:
if block_process: if block_process:
raise StopPropagation from e raise StopPropagation from e
@ -196,12 +195,12 @@ def listener(**args):
await message.edit(lang("run_error"), no_reply=True) # noqa await message.edit(lang("run_error"), no_reply=True) # noqa
if not diagnostics: if not diagnostics:
return return
await Hook.process_error_exec(message, exc_info, exc_format)
if Config.ERROR_REPORT: if Config.ERROR_REPORT:
report = f"""# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n# ChatID: {message.chat.id}. \n# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n# Message: \n-----BEGIN TARGET MESSAGE-----\n{message.text or message.caption}\n-----END TARGET MESSAGE-----\n# Traceback: \n-----BEGIN TRACEBACK-----\n{str(exc_format)}\n-----END TRACEBACK-----\n# Error: "{str(exc_info)}". \n""" report = f"""# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n# ChatID: {message.chat.id}. \n# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n# Message: \n-----BEGIN TARGET MESSAGE-----\n{message.text or message.caption}\n-----END TARGET MESSAGE-----\n# Traceback: \n-----BEGIN TRACEBACK-----\n{str(exc_format)}\n-----END TRACEBACK-----\n# Error: "{str(exc_info)}". \n"""
await attach_report(report, f"exception.{time()}.pgp.txt", None, await attach_report(report, f"exception.{time()}.pgp.txt", None,
"PGP Error report generated.") "PGP Error report generated.")
await Hook.process_error_exec(message, command, exc_info, exc_format)
if (message.chat.id, message.id) in read_context: if (message.chat.id, message.id) in read_context:
del read_context[(message.chat.id, message.id)] del read_context[(message.chat.id, message.id)]
if block_process: if block_process:
@ -274,9 +273,7 @@ def raw_listener(filter_s):
await process_exit(start=False, _client=client, message=message) await process_exit(start=False, _client=client, message=message)
await Hook.shutdown() await Hook.shutdown()
sys.exit(0) sys.exit(0)
except UserNotParticipant: except (UserNotParticipant, MessageNotModified, MessageEmpty, Flood, Forbidden):
pass
except MessageEmpty:
pass pass
except BaseException: except BaseException:
exc_info = sys.exc_info()[1] exc_info = sys.exc_info()[1]

View File

@ -0,0 +1,29 @@
from pagermaid import Config
from pagermaid.enums import Client, Message
from pagermaid.hook import Hook
from mixpanel import Mixpanel
mp = Mixpanel(Config.MIXPANEL_API)
@Hook.on_startup()
async def mixpanel_init_id(bot: Client):
me = await bot.get_me()
if me.username:
mp.people_set(str(me.id), {'$first_name': me.first_name, "username": me.username})
else:
mp.people_set(str(me.id), {'$first_name': me.first_name})
@Hook.command_postprocessor()
async def mixpanel_report(bot: Client, message: Message, command):
if not Config.ALLOW_ANALYTIC:
return
me = await bot.get_me()
sender_id = message.from_user.id if message.from_user else ""
sender_id = message.sender_chat.id if message.sender_chat else sender_id
if sender_id < 0 and message.outgoing:
sender_id = me.id
mp.track(str(sender_id), f'Function {command}', {'command': command, "bot_id": me.id})

View File

@ -4,7 +4,8 @@ from asyncio import sleep
from pagermaid import log from pagermaid import log
from pagermaid.listener import listener from pagermaid.listener import listener
from pagermaid.utils import lang, Message from pagermaid.enums import Client, Message
from pagermaid.utils import lang
import contextlib import contextlib
@ -44,40 +45,31 @@ async def prune(message: Message):
need_admin=True, need_admin=True,
description=lang('sp_des'), description=lang('sp_des'),
parameters=lang('sp_parameters')) parameters=lang('sp_parameters'))
async def self_prune(message: Message): async def self_prune(bot: Client, message: Message):
""" Deletes specific amount of messages you sent. """ """ Deletes specific amount of messages you sent. """
msgs = [] msgs = []
count_buffer = 0 count_buffer = 0
offset = 0
if len(message.parameter) != 1: if len(message.parameter) != 1:
if not message.reply_to_message: if not message.reply_to_message:
return await message.edit(lang('arg_error')) return await message.edit(lang('arg_error'))
async for msg in message.bot.search_messages( offset = message.reply_to_message.id
message.chat.id,
from_user="me",
offset=message.reply_to_message.id,
):
msgs.append(msg.id)
count_buffer += 1
if len(msgs) == 100:
await message.bot.delete_messages(message.chat.id, msgs)
msgs = []
if msgs:
await message.bot.delete_messages(message.chat.id, msgs)
if count_buffer == 0:
await message.delete()
count_buffer += 1
await log(f"{lang('prune_hint1')}{lang('sp_hint')} {str(count_buffer)} {lang('prune_hint2')}")
notification = await send_prune_notify(message, count_buffer, count_buffer)
await sleep(1)
await notification.delete()
return
try: try:
count = int(message.parameter[0]) count = int(message.parameter[0])
await message.delete() await message.delete()
except ValueError: except ValueError:
await message.edit(lang('arg_error')) await message.edit(lang('arg_error'))
return return
async for msg in message.bot.search_messages(message.chat.id, from_user="me"): async for msg in bot.get_chat_history(message.chat.id, limit=100):
if count_buffer == count:
break
if msg.from_user and msg.from_user.is_self:
msgs.append(msg.id)
count_buffer += 1
if len(msgs) == 100:
await message.bot.delete_messages(message.chat.id, msgs)
msgs = []
async for msg in bot.search_messages(message.chat.id, from_user="me", offset=offset):
if count_buffer == count: if count_buffer == count:
break break
msgs.append(msg.id) msgs.append(msg.id)
@ -101,7 +93,7 @@ async def self_prune(message: Message):
need_admin=True, need_admin=True,
description=lang('yp_des'), description=lang('yp_des'),
parameters=lang('sp_parameters')) parameters=lang('sp_parameters'))
async def your_prune(message: Message): async def your_prune(bot: Client, message: Message):
""" Deletes specific amount of messages someone sent. """ """ Deletes specific amount of messages someone sent. """
if not message.reply_to_message: if not message.reply_to_message:
return await message.edit(lang('not_reply')) return await message.edit(lang('not_reply'))
@ -119,18 +111,34 @@ async def your_prune(message: Message):
except Exception: # noqa except Exception: # noqa
pass pass
count_buffer = 0 count_buffer = 0
async for msg in message.bot.search_messages(message.chat.id, from_user=target.from_user.id): msgs = []
async for msg in bot.get_chat_history(message.chat.id, limit=100):
if count_buffer == count:
break
if msg.from_user and msg.from_user.id == target.from_user.id:
msgs.append(msg.id)
count_buffer += 1
if len(msgs) == 100:
await message.bot.delete_messages(message.chat.id, msgs)
msgs = []
async for msg in bot.search_messages(message.chat.id, from_user=target.from_user.id):
if count_buffer == count: if count_buffer == count:
break break
await msg.delete()
count_buffer += 1 count_buffer += 1
msgs.append(msg.id)
if len(msgs) == 100:
await message.bot.delete_messages(message.chat.id, msgs)
msgs = []
if msgs:
await message.bot.delete_messages(message.chat.id, msgs)
await log( await log(
f"{lang('prune_hint1')}{lang('yp_hint')} {str(count_buffer)} / {count} {lang('prune_hint2')}" f"{lang('prune_hint1')}{lang('yp_hint')} {str(count_buffer)} / {count} {lang('prune_hint2')}"
) )
notification = await send_prune_notify(message, count_buffer, count) with contextlib.suppress(ValueError):
await sleep(1) notification = await send_prune_notify(message, count_buffer, count)
await notification.delete() await sleep(1)
await notification.delete()
@listener(is_plugin=False, outgoing=True, command="del", @listener(is_plugin=False, outgoing=True, command="del",

View File

@ -0,0 +1,58 @@
import sentry_sdk
from subprocess import run, PIPE
from time import time
from pyrogram.errors import Unauthorized
from pagermaid import Config
from pagermaid.enums import Client, Message
from pagermaid.hook import Hook
from pagermaid.single_utils import safe_remove
def sentry_before_send(event, hint):
global sentry_sdk_report_time
exc_info = hint.get("exc_info")
if exc_info and isinstance(exc_info[1], Unauthorized):
# The user has been deleted/deactivated or session revoked
safe_remove('pagermaid.session')
exit(1)
if time() <= sentry_sdk_report_time + 30:
sentry_sdk_report_time = time()
return None
else:
sentry_sdk_report_time = time()
return event
sentry_sdk_report_time = time()
sentry_sdk_git_hash = run("git rev-parse HEAD", stdout=PIPE, shell=True).stdout.decode().strip()
sentry_sdk.init(
Config.SENTRY_API,
traces_sample_rate=1.0,
release=sentry_sdk_git_hash,
before_send=sentry_before_send,
environment="production",
)
@Hook.on_startup()
async def sentry_init_id(bot: Client):
me = await bot.get_me()
if me.username:
sentry_sdk.set_user({"id": me.id, "name": me.first_name, "username": me.username, "ip_address": "{{auto}}"})
else:
sentry_sdk.set_user({"id": me.id, "name": me.first_name, "ip_address": "{{auto}}"})
@Hook.process_error()
async def sentry_report(message: Message, command, exc_info, **_):
sender_id = message.from_user.id if message.from_user else ""
sender_id = message.sender_chat.id if message.sender_chat else sender_id
sentry_sdk.set_context("Target", {"ChatID": str(message.chat.id),
"UserID": str(sender_id),
"Msg": message.text or ""})
if command:
sentry_sdk.set_tag("com", command)
sentry_sdk.capture_exception(exc_info)

View File

@ -8,4 +8,6 @@ psutil>=5.8.0
httpx httpx
apscheduler apscheduler
sqlitedict sqlitedict
casbin==1.16.9 casbin==1.16.11
mixpanel
sentry-sdk==1.9.0