Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- cancel, // now remember cancellation permanently - reactions fix now remembers message content hashes after a restart. This means no more 'Edit message again to evaluate' messages - implemented simple key-value store for storing various data - `tgpy.api.config.get(key: str, default: JSON = None) -> JSON` - `tgpy.api.config.set(key: str, value: JSON)` - `tgpy.api.config.unset(key: str)` - `tgpy.api.config.save()` useful when modifying objects acquired using the .get method in place - if the `__all__` variable is set in a module, only objects with names in that list are exported (added to variables list) - `ctx.is_module` is True if the code is executed as a module (on startup) - `ctx.is_manual_output` can be set to True to prevent the last message edit by TGPy so you can edit it yourself - cancel, //, restart, ping, update, modules object and .await syntax are now implemented as regular modules in the `std` directory - restructure the code - `tgpy.api` module is now used for public API instead of the `tgpy` object - new public API functions: - `async parse_code(text: str) -> ParseResult(is_code: bool, original: str, transformed: str, tree: AST | None)` - `parse_tgpy_message(message: Message) -> MessageParseResult(is_tgpy_message: bool, code: str | None, result: str | None)` - `async tgpy_eval(code: str, message: Message = None, *, filename: str = None) -> EvalResult(result: Any, output: str)` - `apply_code_transformers(code: str) -> str` - AST transformers. AST transformers are applied after code transformers. API functions: - `add_ast_transformer(name: str, transformer)` - `async apply_ast_transformers(tree: AST) -> AST` - exec hooks. Exec hooks are executed before the message is parsed and handled. Exec hooks must have the following signature: `async hook(message: Message, is_edit: bool) -> Message | bool | None`. An exec hook may edit the message using Telegram API methods and/or alter the message in place. If a hook returns Message object or alters it in place, it's used instead of the original Message object during the rest of handling (including calling other hook functions). If a hook returns True or None, execution completes normally. If a hook returns False, the rest of hooks are executed and then the handling stops without further message parsing or evaluating. Exec hooks API methods: - `add_exec_hook(name: str, hook)` - `apply_exec_hooks(message: Message, *, is_edit: bool) -> Message | False` returns False if any of the hooks returned False, Message object that should be used instead of the original one otherwise
- Loading branch information
Showing
35 changed files
with
969 additions
and
578 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ dist/ | |
*.session | ||
*.session-journal | ||
data/ | ||
config.py | ||
config.yaml | ||
|
||
guide/site |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,26 @@ | ||
import logging | ||
|
||
from rich.logging import RichHandler | ||
from telethon import TelegramClient | ||
|
||
from tgpy.api import API | ||
from tgpy.app_config import Config | ||
from tgpy.console import console | ||
from tgpy.context import Context | ||
from tgpy.version import __version__ | ||
|
||
logging.basicConfig( | ||
level=logging.INFO, format='%(message)s', datefmt="[%X]", handlers=[RichHandler()] | ||
format='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s', | ||
datefmt='%Y-%m-%d %H:%M:%S', | ||
level=logging.INFO, | ||
) | ||
logging.getLogger('telethon').setLevel(logging.WARNING) | ||
|
||
|
||
class App: | ||
config: Config = None | ||
client: TelegramClient = None | ||
api: API = None | ||
client: TelegramClient | ||
ctx: Context | ||
|
||
def __init__(self): | ||
self.ctx = Context() | ||
self.api = API() | ||
|
||
|
||
app = App() | ||
|
||
__all__ = ['App', 'app'] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from telethon.errors import MessageIdInvalidError | ||
from telethon.tl.custom import Message | ||
|
||
from tgpy import app | ||
from tgpy._core import message_design | ||
from tgpy._core.utils import convert_result, format_traceback | ||
from tgpy.api.tgpy_eval import tgpy_eval | ||
|
||
|
||
async def eval_message(code: str, message: Message) -> Message | None: | ||
await message_design.edit_message(message, code, 'Running...') | ||
|
||
# noinspection PyBroadException | ||
try: | ||
eval_result = await tgpy_eval(code, message, filename=None) | ||
except Exception: | ||
result = 'Error occurred' | ||
output = '' | ||
exc = ''.join(format_traceback()) | ||
else: | ||
if app.ctx.is_manual_output: | ||
return | ||
result = convert_result(eval_result.result) | ||
output = eval_result.output | ||
exc = '' | ||
|
||
try: | ||
# noinspection PyProtectedMember | ||
return await message_design.edit_message( | ||
message, | ||
code, | ||
result, | ||
traceback=exc, | ||
output=output, | ||
) | ||
except MessageIdInvalidError: | ||
return None | ||
|
||
|
||
__all__ = ['eval_message'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import sys | ||
import tokenize | ||
import traceback | ||
from io import BytesIO | ||
|
||
from telethon.tl import TLObject | ||
|
||
|
||
def convert_result(result): | ||
if isinstance(result, TLObject): | ||
result = result.stringify() | ||
|
||
return result | ||
|
||
|
||
def format_traceback(): | ||
exc_type, exc_value, exc_traceback = sys.exc_info() | ||
exc_traceback = exc_traceback.tb_next.tb_next | ||
return traceback.format_exception(exc_type, exc_value, exc_traceback) | ||
|
||
|
||
def tokenize_string(s: str) -> list[tokenize.TokenInfo] | None: | ||
try: | ||
return list(tokenize.tokenize(BytesIO(s.encode('utf-8')).readline)) | ||
except (IndentationError, tokenize.TokenError): | ||
return None | ||
|
||
|
||
def untokenize_to_string(tokens: list[tokenize.TokenInfo]) -> str: | ||
return tokenize.untokenize(tokens).decode('utf-8') |
Oops, something went wrong.