From 34c567c1f2aeb6086727465f7c6587bc202611aa Mon Sep 17 00:00:00 2001
From: Xtao_dada
Date: Fri, 3 Sep 2021 19:03:34 +0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=F0=9F=9A=80=20fix=20some=20bugs=20?=
=?UTF-8?q?and=20support=20for=20deploy=20to=20railway=20(#128)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
🐛 批量修复错误
🚀 支持云部署到 railway
---
README.md | 21 +++++
pagermaid/__init__.py | 97 +++++++++++++++--------
pagermaid/interface/__init__.py | 18 ++++-
pagermaid/modules/external.py | 4 +
pagermaid/modules/sb.py | 2 +
pagermaid/modules/sticker.py | 8 +-
pagermaid/static/images/icon.jpg | Bin 0 -> 19561 bytes
pagermaid/templates/includes/navbar.html | 4 +-
utils/gensession.py | 44 ++++++++++
9 files changed, 157 insertions(+), 41 deletions(-)
create mode 100644 pagermaid/static/images/icon.jpg
create mode 100644 utils/gensession.py
diff --git a/README.md b/README.md
index e69381c..d8793a3 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,9 @@
+
+
@@ -29,6 +31,25 @@ Pagermaid 是一个用在 Telegram 的实用工具。
[Docker安装](utils/docker.md)
+## 云部署
+
+### 必须变量
+
+- `API_ID` - 到 [my.telegram.org](https://my.telegram.org/) 申请获得的值
+- `API_HASH` - 到 [my.telegram.org](https://my.telegram.org/) 申请获得的值
+- `SESSION` - 账户授权字符,您需要[到这里](#Session)获取
+
+[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FXtao-Labs%2FPagerMaid-Modify%2Ftree%2Fmaster&envs=session%2Capi_key%2Capi_hash&sessionDesc=Your+telethon+session+string.&api_keyDesc=api_id%2C+from+my.telegram.org&api_hashDesc=api_hash%2C+from+my.telegram.org&referralCode=xtaolabs)
+
+# Session
+
+> 这是您账户的授权文件,请妥善保管
+
+获得 `API_ID` 和 `API_HASH` 后,你可以通过下面两种方式获取 `SESSION`:
+
+* 在线获取:[![Repl.it](https://replit.com/badge/github/Xtao-Labs/PagerMaid-Modify)](https://replit.com/@mrwangzhe/gensession)
+* 本地获取:`cd utils && python3 -m pip install telethon && python3 gensession.py`
+
# 对存在使用本项目用户群组的提醒
由于本项目需要响应账号通过其他客户端发出的命令,所以在本项目正常运行时,可能使用下列信息:
diff --git a/pagermaid/__init__.py b/pagermaid/__init__.py
index 58751dd..4522f52 100644
--- a/pagermaid/__init__.py
+++ b/pagermaid/__init__.py
@@ -15,7 +15,7 @@ except:
pass
from subprocess import run, PIPE
from time import time
-from os import getcwd, makedirs
+from os import getcwd, makedirs, environ
from os.path import exists
from sys import version_info, platform
from yaml import load, FullLoader, safe_load
@@ -26,11 +26,13 @@ from logging import getLogger, INFO, DEBUG, ERROR, StreamHandler, basicConfig
from distutils.util import strtobool
from coloredlogs import ColoredFormatter
from telethon import TelegramClient
+from telethon.sessions import StringSession
# Errors
from telethon.errors.rpcerrorlist import MessageNotModifiedError, MessageIdInvalidError, ChannelPrivateError, \
ChatSendMediaForbiddenError, YouBlockedUserError, FloodWaitError, ChatWriteForbiddenError, \
- AuthKeyDuplicatedError, ChatSendStickersForbiddenError
+ AuthKeyDuplicatedError, ChatSendStickersForbiddenError, SlowModeWaitError, MessageEditTimeExpiredError, \
+ PeerIdInvalidError
from telethon.errors.common import AlreadyInConversationError
from requests.exceptions import ChunkedEncodingError
from requests.exceptions import ConnectionError as ConnectedError
@@ -141,6 +143,25 @@ if not exists(f"{getcwd()}/data"):
api_key = config['api_key']
api_hash = config['api_hash']
+session_string = "pagermaid"
+# environ
+if environ.get('api_key'):
+ api_key = environ.get('api_key')
+if environ.get('api_hash'):
+ api_hash = environ.get('api_hash')
+if environ.get('session'):
+ string_session = environ.get('session')
+ session_string = StringSession(string_session)
+# api type
+try:
+ api_key = int(api_key)
+except ValueError:
+ logs.info(
+ lang('config_error')
+ )
+ exit(1)
+except:
+ pass
try:
proxy_addr = config['proxy_addr'].strip()
proxy_port = config['proxy_port'].strip()
@@ -192,13 +213,13 @@ if not proxy_addr == '' and not proxy_port == '':
"http": f"socks5://{proxy_addr}:{proxy_port}",
"https": f"socks5://{proxy_addr}:{proxy_port}"
}
- bot = TelegramClient("pagermaid", api_key, api_hash,
+ bot = TelegramClient(session_string, api_key, api_hash,
auto_reconnect=True,
proxy=(python_socks.ProxyType.SOCKS5, proxy_addr, int(proxy_port)),
use_ipv6=use_ipv6)
except:
proxies = {}
- bot = TelegramClient("pagermaid", api_key, api_hash,
+ bot = TelegramClient(session_string, api_key, api_hash,
auto_reconnect=True,
use_ipv6=use_ipv6)
elif not http_addr == '' and not http_port == '':
@@ -209,24 +230,24 @@ elif not http_addr == '' and not http_port == '':
"http": f"http://{http_addr}:{http_port}",
"https": f"http://{http_addr}:{http_port}"
}
- bot = TelegramClient("pagermaid", api_key, api_hash,
+ bot = TelegramClient(session_string, api_key, api_hash,
auto_reconnect=True,
proxy=(python_socks.ProxyType.HTTP, http_addr, int(http_port)),
use_ipv6=use_ipv6)
except:
- bot = TelegramClient("pagermaid", api_key, api_hash,
+ bot = TelegramClient(session_string, api_key, api_hash,
auto_reconnect=True,
use_ipv6=use_ipv6)
elif not mtp_addr == '' and not mtp_port == '' and not mtp_secret == '':
from telethon import connection
- bot = TelegramClient("pagermaid", api_key, api_hash,
+ bot = TelegramClient(session_string, api_key, api_hash,
auto_reconnect=True,
connection=connection.ConnectionTcpMTProxyRandomizedIntermediate,
proxy=(mtp_addr, int(mtp_port), mtp_secret),
use_ipv6=use_ipv6)
else:
- bot = TelegramClient("pagermaid", api_key, api_hash, auto_reconnect=True, use_ipv6=use_ipv6)
+ bot = TelegramClient(session_string, api_key, api_hash, auto_reconnect=True, use_ipv6=use_ipv6)
user_id = 0
redis = StrictRedis(host=redis_host, port=redis_port, db=redis_db)
@@ -259,59 +280,65 @@ def before_send(event, hint):
global report_time
exc_info = hint.get("exc_info")
if exc_info and isinstance(exc_info[1], ConnectionError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], CancelledError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], MessageNotModifiedError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], MessageIdInvalidError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], OperationalError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ChannelPrivateError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], BufferError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], RemoteDisconnected):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ChatSendMediaForbiddenError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], TypeError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], URLError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], YouBlockedUserError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], FloodWaitError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ChunkedEncodingError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], TimeoutError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], UnicodeEncodeError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ChatWriteForbiddenError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ChatSendStickersForbiddenError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], AlreadyInConversationError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ConnectedError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], KeyboardInterrupt):
- return None
+ return
elif exc_info and isinstance(exc_info[1], OSError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], AuthKeyDuplicatedError):
- return None
+ return
elif exc_info and isinstance(exc_info[1], ResponseError):
- return None
+ return
+ elif exc_info and isinstance(exc_info[1], SlowModeWaitError):
+ return
+ elif exc_info and isinstance(exc_info[1], MessageEditTimeExpiredError):
+ return
+ elif exc_info and isinstance(exc_info[1], PeerIdInvalidError):
+ return
if not python36:
if exc_info and isinstance(exc_info[1], CancelError):
- return None
+ return
if time() <= report_time + 30:
report_time = time()
- return None
+ return
else:
report_time = time()
return event
@@ -320,7 +347,7 @@ def before_send(event, hint):
report_time = time()
git_hash = run("git rev-parse HEAD", stdout=PIPE, shell=True).stdout.decode()
sentry_sdk.init(
- "https://a0e1ef3c67ca48f4b1ecfc5538528ef3@o416616.ingest.sentry.io/5312335",
+ "https://935d04099b7d4bd889e7ffac488579fc@o416616.ingest.sentry.io/5312335",
traces_sample_rate=1.0,
release=git_hash,
before_send=before_send,
diff --git a/pagermaid/interface/__init__.py b/pagermaid/interface/__init__.py
index ab77e0f..cb8d04a 100644
--- a/pagermaid/interface/__init__.py
+++ b/pagermaid/interface/__init__.py
@@ -1,5 +1,6 @@
""" PagerMaid web interface utility. """
+from os import environ
from threading import Thread
from distutils.util import strtobool
from importlib import import_module
@@ -36,12 +37,23 @@ import_module('pagermaid.interface.views')
import_module('pagermaid.interface.modals')
dispatcher = PathInfoDispatcher({'/': app})
-server = WSGIServer((config['web_interface']['host'], int(config['web_interface']['port'])), dispatcher)
+web_host = config['web_interface']['host']
+try:
+ web_port = int(config['web_interface']['port'])
+except ValueError:
+ web_port = 3333
+if environ.get('PORT'):
+ web_host = '0.0.0.0'
+ try:
+ web_port = int(environ.get('PORT'))
+ except ValueError:
+ web_port = 3333
+server = WSGIServer((web_host, web_port), dispatcher)
def start():
- if strtobool(config['web_interface']['enable']):
- logs.info(f"已经启动Web界面 {config['web_interface']['host']}:{config['web_interface']['port']}")
+ if strtobool(config['web_interface']['enable']) or environ.get('PORT'):
+ logs.info(f"已经启动Web界面 {web_host}:{web_port}")
app.logger.removeHandler(default_handler)
app.logger.addHandler(logging_handler)
try:
diff --git a/pagermaid/modules/external.py b/pagermaid/modules/external.py
index 1a5d4b7..8ebd7bb 100644
--- a/pagermaid/modules/external.py
+++ b/pagermaid/modules/external.py
@@ -4,6 +4,7 @@ from pygoogletranslation import Translator
from os import remove
from magic_google import MagicGoogle
from gtts import gTTS
+from gtts.tts import gTTSError
from re import compile as regex_compile
from pagermaid import log
from pagermaid.listener import listener, config
@@ -94,6 +95,9 @@ async def tts(context):
except ConnectionError:
await context.edit(lang('tts_RuntimeError'))
return
+ except gTTSError:
+ await context.edit(lang('tts_RuntimeError'))
+ return
with open("vocals.mp3", "rb") as audio:
line_list = list(audio)
line_count = len(line_list)
diff --git a/pagermaid/modules/sb.py b/pagermaid/modules/sb.py
index 780d3f2..6879a30 100644
--- a/pagermaid/modules/sb.py
+++ b/pagermaid/modules/sb.py
@@ -104,6 +104,8 @@ async def span_ban(context):
pass
except ChatAdminRequiredError:
pass
+ except ValueError:
+ pass
if redis_status():
sb_groups = redis.get('sb_groups')
if sb_groups:
diff --git a/pagermaid/modules/sticker.py b/pagermaid/modules/sticker.py
index e9f9012..eda18b4 100644
--- a/pagermaid/modules/sticker.py
+++ b/pagermaid/modules/sticker.py
@@ -5,7 +5,7 @@ from bs4 import BeautifulSoup
from asyncio import sleep
from os import remove
from io import BytesIO
-from telethon.tl.types import DocumentAttributeFilename, MessageMediaPhoto, MessageMediaWebPage
+from telethon.tl.types import DocumentAttributeFilename, MessageMediaPhoto, MessageMediaWebPage, MessageMediaDice
from telethon.tl.functions.contacts import UnblockRequest
from telethon.errors.common import AlreadyInConversationError
from PIL import Image, ImageOps
@@ -203,6 +203,12 @@ async def single_sticker(animated, context, custom_emoji, emoji, message, pic_ro
except:
pass
return
+ elif isinstance(message.media, MessageMediaDice):
+ try:
+ await context.edit(lang('sticker_type_not_support'))
+ except:
+ pass
+ return
elif "image" in message.media.document.mime_type.split('/'):
photo = BytesIO()
try:
diff --git a/pagermaid/static/images/icon.jpg b/pagermaid/static/images/icon.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fd1b8b034b121b1ac7ee4f6839f68ee7682c1eca
GIT binary patch
literal 19561
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
Nor^Ks{{Y0F|Jf~A7*PNK
literal 0
HcmV?d00001
diff --git a/pagermaid/templates/includes/navbar.html b/pagermaid/templates/includes/navbar.html
index d59e5a7..cb3bbf9 100644
--- a/pagermaid/templates/includes/navbar.html
+++ b/pagermaid/templates/includes/navbar.html
@@ -1,6 +1,6 @@