Skip to content

Commit

Permalink
v3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Bibo-Joshi committed Jan 6, 2023
1 parent d39dc60 commit 2133e0a
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 81 deletions.
27 changes: 19 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,24 @@ repos:
# run pylint across multiple cpu cores to speed it up-
- --jobs=0 # See https://pylint.pycqa.org/en/latest/user_guide/run.html?#parallel-execution to know more
additional_dependencies:
- python-telegram-bot==20.0a0
- Pillow==9.1.1
- python-telegram-bot[rate-limiter]~=20.0
- Pillow~=9.3
- PyHyphen==4.0.3
- pytz
- thefuzz==0.19.0
- git+https://github.com/Bibo-Joshi/ptbstats.git@v2.0
- thefuzz~=0.19
- git+https://github.com/Bibo-Joshi/ptbstats.git@v2.1
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
files: ^(main|bot/\w*).py$
additional_dependencies:
- python-telegram-bot==20.0a0
- Pillow==9.1.1
- python-telegram-bot[rate-limiter]~=20.0
- Pillow~=9.3
- PyHyphen==4.0.3
- pytz
- thefuzz==0.19.0
- git+https://github.com/Bibo-Joshi/ptbstats.git@v2.0
- thefuzz~=0.19
- git+https://github.com/Bibo-Joshi/ptbstats.git@v2.1
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.0
hooks:
Expand All @@ -55,3 +55,14 @@ repos:
args:
- --diff
- --check
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.210
hooks:
- id: ruff
additional_dependencies:
- python-telegram-bot[rate-limiter]~=20.0
- Pillow~=9.3
- PyHyphen==4.0.3
- pytz
- thefuzz~=0.19
- git+https://github.com/Bibo-Joshi/[email protected]
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
Changelog
=========

Version 3.1
===========
*Released 2023-01-01*

* Upgrade ``python-telegram-bot`` to v20.0
* Upgrade ``ptbstats`` to v2.1
* Fix a bug where the ``toggle_store_stickers`` setting was not respected for text stickers
* Improve the error handler

Version 3.0
===========
*Released 2022-05-22*
Expand Down
4 changes: 3 additions & 1 deletion bot/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ async def sticker_message(update: Update, context: CCT) -> None:
context.application.create_task(context.bot.send_chat_action(user.id, ChatAction.UPLOAD_PHOTO))
stream = await get_sticker_photo_stream(cast(str, msg.text), user, context)
sticker = cast(Sticker, (await msg.reply_sticker(stream)).sticker)
cast(UserData, context.user_data).sticker_file_ids[sticker.file_unique_id] = sticker.file_id
user_data = cast(UserData, context.user_data)
if user_data.store_stickers:
user_data.sticker_file_ids[sticker.file_unique_id] = sticker.file_id


async def info(update: Update, context: CCT) -> None:
Expand Down
76 changes: 45 additions & 31 deletions bot/error.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
"""Methods for error handlers."""
import html
import json
import logging
import time
import traceback
from typing import cast
from typing import Optional, cast

from telegram import Update
from telegram.error import BadRequest, Forbidden, RetryAfter
from telegram.error import BadRequest, Forbidden

from bot.constants import ADMIN_KEY
from bot.twitter import HyphenationError
Expand Down Expand Up @@ -47,22 +47,14 @@ async def error(update: object, context: CCT) -> None:
"""
admin_id = cast(int, context.bot_data[ADMIN_KEY])

if not isinstance(context.error, Exception):
return

# Log the error before we do anything else, so we can see it even if something breaks.
logger.error(msg="Exception while handling an update:", exc_info=context.error)

if isinstance(context.error, HyphenationError):
return

if (
isinstance(context.error, BadRequest) and "Query is too old" in str(context.error)
) or isinstance(context.error, Forbidden):
return

if isinstance(context.error, RetryAfter):
time.sleep(int(context.error.retry_after) + 2)
isinstance(context.error, (HyphenationError, Forbidden))
or (isinstance(context.error, BadRequest) and "Query is too old" in str(context.error))
or context.error is None
):
return

# Inform sender of update, that something went wrong
Expand All @@ -72,21 +64,43 @@ async def error(update: object, context: CCT) -> None:

# Get traceback
tb_list = traceback.format_exception(None, context.error, context.error.__traceback__)
trace = "".join(tb_list)

# Gather information from the update
payload = ""
if isinstance(update, Update):
if update.effective_user:
payload += f" with the user {update.effective_user.mention_html()}"
if update.effective_chat and update.effective_chat.username:
payload += f" (@{html.escape(update.effective_chat.username)})"
if update.poll:
payload += f" with the poll id {update.poll.id}."
text = (
f"Hey.\nThe error <code>{html.escape(str(context.error))}</code> happened"
f"{payload}. The full traceback:\n\n<code>{html.escape(trace)}</code>"
tb_string = "".join(tb_list)

# Build the message with some markup and additional information about what happened.
update_str = update.to_dict() if isinstance(update, Update) else str(update)
message_1 = (
f"An exception was raised while handling an update\n\n"
f"<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}</pre>"
)
message_2 = f"<pre>{html.escape(tb_string)}</pre>"

user_id: Optional[int] = None
message_3 = ""
if isinstance(update, Update) and update.effective_user:
user_id = update.effective_user.id
if context.job:
user_id = context.job.user_id
if user_id:
data_str = html.escape(
json.dumps(context.application.user_data[user_id], indent=2, ensure_ascii=False)
)
message_3 = (
"<code>user_data</code> associated with this exception:\n\n" f"<pre>{data_str}</pre>"
)

# Send to admin
await context.bot.send_message(admin_id, text)
# Finally, send the messages
# We send update and traceback in two parts to reduce the chance of hitting max length
sent_message = await context.bot.send_message(chat_id=admin_id, text=message_1)
try:
await sent_message.reply_html(message_2)
except BadRequest as exc:
if "too long" not in str(exc):
raise exc
message = (
f"Hey.\nThe error <code>{html.escape(str(context.error))}</code> happened."
f" The traceback is too long to send, but it was written to the log."
)
sent_message = await context.bot.send_message(chat_id=admin_id, text=message)
finally:
if message_3:
await sent_message.reply_html(message_3)
37 changes: 13 additions & 24 deletions bot/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,6 @@ async def setup_application(
admin_id: The admins chat id.
sticker_chat_id: The name of the chat where stickers can be sent to get their file IDs.
"""
async with application:
await _setup_application(
application=application, admin_id=admin_id, sticker_chat_id=sticker_chat_id
)


async def _setup_application(
application: Application[ExtBot, CCT, UserData, dict, dict, JobQueue],
admin_id: int,
sticker_chat_id: Union[str, int],
) -> None:
# error handlers
application.add_error_handler(hyphenation_error)
application.add_error_handler(error)
Expand Down Expand Up @@ -119,23 +108,23 @@ def check_text(update: object) -> bool:

# Bot commands
base_commands = [
["help", "Displays a short info message about the Twitter Status Bot"],
["start", 'See "/help"'],
["info", 'Same as "/help"'],
["toggle_store_stickers", "(De)activates the saving of stickers"],
["delete_sticker", "Deletes one specific stored sticker"],
["set_fallback_picture", "Sets fallback profile picture"],
["delete_fallback_picture", "Deletes fallback profile picture"],
["show_fallback_picture", "Shows current fallback profile picture"],
["set_timezone", "Sets the timezone used for the stickers"],
[
("help", "Displays a short info message about the Twitter Status Bot"),
("start", 'See "/help"'),
("info", 'Same as "/help"'),
("toggle_store_stickers", "(De)activates the saving of stickers"),
("delete_sticker", "Deletes one specific stored sticker"),
("set_fallback_picture", "Sets fallback profile picture"),
("delete_fallback_picture", "Deletes fallback profile picture"),
("show_fallback_picture", "Shows current fallback profile picture"),
("set_timezone", "Sets the timezone used for the stickers"),
(
"toggle_text_direction",
"Changes sticker text direction from right-to-left to left-to-right and vice versa",
],
),
]
admin_commands = [
["ilq", "Show Statistics for inline requests"],
["text", "Show Statistics for text requests"],
("ilq", "Show Statistics for inline requests"),
("text", "Show Statistics for text requests"),
]

await application.bot.set_my_commands(base_commands)
Expand Down
2 changes: 1 addition & 1 deletion bot/twitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ async def get_header( # pylint: disable = R0914
photo_file = await bot.get_file(file_id)
picture_stream = BytesIO()
_check_event(event)
await photo_file.download(out=picture_stream)
await photo_file.download_to_memory(out=picture_stream)
picture_stream.seek(0)
_check_event(event)
user_picture = Image.open(picture_stream)
Expand Down
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
copyright = "2020, Hinrich Mahler"
author = "Hinrich Mahler"
master_doc = "index"
version = "3.0"
release = "3.0"
version = "3.1"
release = "3.1"


# -- General configuration ---------------------------------------------------
Expand Down
33 changes: 25 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#!/usr/bin/env python3
"""The script that runs the bot."""
import asyncio
import functools
import logging
from configparser import ConfigParser

from telegram.constants import ParseMode
from telegram.ext import Application, ContextTypes, Defaults, PersistenceInput, PicklePersistence
from telegram.ext import (
AIORateLimiter,
Application,
ContextTypes,
Defaults,
PersistenceInput,
PicklePersistence,
)

from bot.setup import setup_application
from bot.userdata import UserData
Expand Down Expand Up @@ -45,16 +52,26 @@ def main() -> None:
.defaults(defaults)
.persistence(persistence)
.context_types(context_types)
.post_init(
functools.partial(
setup_application, admin_id=admin_id, sticker_chat_id=sticker_chat_id
)
)
.rate_limiter(
# Don't apply rate limiting, but retry on `RetryAfter` errors
AIORateLimiter(
overall_max_rate=0,
overall_time_period=0,
group_max_rate=0,
group_time_period=0,
max_retries=3,
)
)
.build()
)

# Get the application to register handlers
asyncio.get_event_loop().run_until_complete(
setup_application(application, admin_id, sticker_chat_id)
)

# Start the Bot
application.run_polling(drop_pending_updates=True, close_loop=False)
application.run_polling(drop_pending_updates=True)


if __name__ == "__main__":
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ target-version = ['py37', 'py38', 'py39', 'py310']

[tool.isort] # black config
profile = "black"
line_length = 99
line_length = 99

[tool.ruff]
line-length = 99
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pre-commit
pre-commit~=2.21
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
python-telegram-bot==20.0a0
Pillow==9.3.0
python-telegram-bot[rate-limiter]~=20.0
Pillow~=9.3
PyHyphen==4.0.3
pytz
thefuzz==0.19.0
git+https://github.com/Bibo-Joshi/ptbstats.git@v2.0.2
thefuzz~=0.19
git+https://github.com/Bibo-Joshi/ptbstats.git@v2.1

0 comments on commit 2133e0a

Please sign in to comment.