Skip to content

Commit 111fc83

Browse files
committed
Update to Python 3.12, python-telegram-bot 21.4
1 parent 6ef6ee5 commit 111fc83

9 files changed

Lines changed: 203 additions & 557 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ virtualenv
77
.DS_Store
88
.env
99
.pytest_cache
10+
.python-version

Pipfile

Lines changed: 0 additions & 18 deletions
This file was deleted.

Pipfile.lock

Lines changed: 0 additions & 434 deletions
This file was deleted.

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,19 @@ Telegram Bot made in Python to automate different tasks of [Python Canarias](htt
99
1010
## Installation
1111

12-
Create the virtualenv for Python3 and install dependencies with:
12+
Create a virtualenv for Python3 and install dependencies. In this
13+
example we are using pyenv:
1314

1415
~~~console
15-
$ pipenv install
16+
$ pyenv virtualenv 3.12.4 pydeckard
17+
$ pyenv activate pydeckard
18+
$ pip install -r requirements.txt
19+
~~~
20+
21+
A developer needs to install a few more packages:
22+
23+
~~~console
24+
$ pip install -r dev-requirements.txt
1625
~~~
1726

1827
Next step is to set your bot token for development:
@@ -27,9 +36,17 @@ Now you can launch the bot with:
2736
$ python bot.py
2837
~~~
2938

39+
You can use the flag `--verbose` (or `-v') to get more information in rhe console:
40+
41+
~~~console
42+
$ python bot.py --verbose
43+
~~~
44+
45+
3046
## Tests
3147

48+
Use pytest:
49+
3250
~~~console
33-
$ pipenv install --dev
34-
$ pytest
51+
$ python -m pytest
3552
~~~

bot.py

Lines changed: 168 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,182 @@
1+
#!/usr/bin/enb python3
2+
3+
from datetime import datetime as DateTime
4+
import argparse
15
import logging
2-
from time import sleep
6+
import sys
7+
import time
38

4-
import telegram
5-
from telegram.ext import Updater, Filters, MessageHandler, CommandHandler
69
from telegram import Update
10+
from telegram.constants import ParseMode
11+
from telegram.ext import ApplicationBuilder
12+
from telegram.ext import CommandHandler
13+
from telegram.ext import MessageHandler
14+
from telegram.ext import ContextTypes
15+
from telegram.ext import filters
716

817
import config
918
import utils
1019

1120

12-
logger = logging.getLogger('bot')
13-
14-
15-
def command_start(update, context):
16-
logger.info('Received command /start')
17-
context.bot.send_message(chat_id=update.message.chat_id, text=config.BOT_GREETING)
18-
19-
20-
def command_help(update, context):
21-
logger.info('Received command /help')
22-
context.bot.send_message(
23-
chat_id=update.message.chat_id,
24-
text="Available commands:\n"
25-
" - /start - start intereaction with the bot\n"
26-
" - /help - Show commands\n"
27-
" - /status - Show status and alive time\n"
28-
)
29-
30-
31-
def command_status(update, context):
32-
logger.info('bot asked to execute /status commamd')
33-
context.bot.send_message(
34-
chat_id=update.message.chat_id,
35-
text=f'Status is OK, running since {utils.since()}',
36-
)
37-
38-
39-
def welcome(update: Update, context):
40-
logger.info('Received new user event')
41-
new_member = update.message.new_chat_members[0]
42-
43-
logger.info(f'Waiting {config.WELCOME_DELAY} seconds until user completes captcha...')
44-
sleep(config.WELCOME_DELAY)
45-
membership_info = context.bot.get_chat_member(update.message.chat_id, new_member.id)
46-
if membership_info['status'] == 'left':
47-
logger.info(f'Skipping welcome message, user {new_member.name} is no longer in the chat')
48-
return
49-
50-
logger.info(f'send welcome message for {new_member.name}')
51-
msg = None
52-
53-
if new_member.is_bot:
54-
msg = f"{new_member.name} is a *bot*!! " \
55-
"-> It could be kindly removed 🗑"
56-
else:
57-
if utils.is_bot(new_member):
58-
context.bot.delete_message(update.message.chat_id,
59-
update.message.message_id)
60-
if context.bot.kick_chat_member(update.message.chat_id, new_member.id):
61-
msg = (f"*{new_member.username}* has been banned because I "
62-
"considered it was a bot. ")
21+
class DeckardBot():
22+
23+
def __init__(self):
24+
self.get_options()
25+
self.set_logger()
26+
self.started_at = DateTime.now()
27+
28+
def get_options(self):
29+
parser = argparse.ArgumentParser(
30+
prog='bot',
31+
description='PyDeckard Bot',
32+
epilog='Text at the bottom of help',
33+
)
34+
parser.add_argument('-v', '--verbose', action='store_true')
35+
args = parser.parse_args()
36+
self.verbose = args.verbose
37+
38+
def set_logger(self):
39+
self.logger = logging.getLogger('bot')
40+
logging.basicConfig(
41+
level=config.LOG_LEVEL,
42+
format='%(asctime)s [%(name)s] %(levelname)s: %(message)s',
43+
)
44+
config.log(self.logger.info)
45+
46+
def trace(self, msg):
47+
self.logger.info('bot asked to execute /status commamd')
48+
if self.verbose:
49+
print(msg)
50+
51+
async def command_status(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
52+
self.trace('bot asked to execute /status commamd')
53+
python_version = sys.version.split(maxsplit=1)[0]
54+
text = '\n'.join([
55+
config.BOT_GREETING,
56+
f'Status is <b>OK</b>, running since {utils.since(self.started_at)}',
57+
f'Python version is {python_version}',
58+
])
59+
await context.bot.send_message(
60+
chat_id=update.effective_chat.id,
61+
text=text,
62+
parse_mode=ParseMode.HTML,
63+
)
64+
self.trace(text)
65+
66+
async def command_start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
67+
self.trace('Received command /start')
68+
await context.bot.send_message(
69+
chat_id=update.effective_chat.id,
70+
text=config.BOT_GREETING,
71+
parse_mode=ParseMode.HTML,
72+
)
73+
74+
async def command_help(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
75+
self.trace('Received command /help')
76+
await context.bot.send_message(
77+
chat_id=update.effective_chat.id,
78+
text=(
79+
"Available commands:\n\n"
80+
"<code>/start</code> : start intereaction with the bot\n"
81+
"<code>/help</code> : Show commands\n"
82+
"<code>/status</code> : Show status and alive time\n"
83+
"<code>/zen</code> : Show the Zen of Python\n"
84+
),
85+
parse_mode=ParseMode.HTML,
86+
)
87+
88+
async def command_zen(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
89+
self.trace('Received command /zen')
90+
text = '\n'.join(config.THE_ZEN_OF_PYTHON)
91+
await context.bot.send_message(
92+
chat_id=update.effective_chat.id,
93+
text=text,
94+
parse_mode=ParseMode.HTML,
95+
)
96+
97+
async def welcome(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
98+
self.trace('Received new user event')
99+
new_member = update.message.new_chat_members[0]
100+
self.trace(
101+
f'Waiting {config.WELCOME_DELAY} seconds'
102+
' until user completes captcha...'
103+
)
104+
time.sleep(config.WELCOME_DELAY)
105+
membership_info = context.bot.get_chat_member(
106+
update.message.chat_id,
107+
new_member.id,
108+
)
109+
if membership_info['status'] == 'left':
110+
self.trace('Skipping welcome message, user is no longer in the chat')
111+
return
112+
113+
self.trace(f'send welcome message for {new_member.name}')
114+
115+
msg = f"Welcome {new_member.name}!! I am a friendly and polite *bot* 🤖"
116+
if new_member.is_bot:
117+
msg = (
118+
f"{new_member.name} looks like a *bot*!! "
119+
"-> It could be kindly removed 🗑"
120+
)
63121
else:
64-
msg = f"Welcome {new_member.name}!! " \
65-
"I am a friendly and polite *bot* 🤖"
66-
if msg:
67-
context.bot.send_message(
122+
if utils.is_bot(new_member):
123+
context.bot.delete_message(
124+
update.message.chat_id,
125+
update.message.message_id,
126+
)
127+
if context.bot.kick_chat_member(
128+
update.message.chat_id,
129+
new_member.id
130+
):
131+
msg = (
132+
f"*{new_member.username}* has been banned because I "
133+
"considered it a bot. "
134+
)
135+
await context.bot.send_message(
68136
chat_id=update.message.chat_id,
69137
text=msg,
70-
parse_mode=telegram.ParseMode.MARKDOWN
71-
)
72-
73-
74-
def reply(update, context):
75-
if not config.bot_replies_enabled():
76-
return
77-
78-
msg = update.message.text
79-
reply_spec = utils.triggers_reply(msg) if msg else None
80-
if reply_spec is not None:
81-
logger.info(f'bot sends reply {reply_spec.reply}')
82-
context.bot.send_message(
83-
chat_id=update.message.chat_id,
84-
text=reply_spec.reply
85-
)
86-
87-
88-
def main():
89-
logging.basicConfig(
90-
level=config.LOG_LEVEL,
91-
format='%(asctime)s [%(name)s] %(levelname)s: %(message)s',
92-
)
93-
logger.info('Starting bot...')
94-
config.log(logger.info)
95-
updater = Updater(config.TELEGRAM_BOT_TOKEN)
96-
dp = updater.dispatcher
97-
98-
dp.add_handler(CommandHandler('start', command_start))
99-
dp.add_handler(CommandHandler('help', command_help))
100-
dp.add_handler(CommandHandler('status', command_status))
101-
dp.add_handler(MessageHandler(Filters.status_update.new_chat_members,
102-
welcome, run_async=True))
103-
dp.add_handler(MessageHandler(Filters.chat_type.groups, reply))
104-
105-
logger.info('Bot is ready')
106-
updater.start_polling(poll_interval=config.POLL_INTERVAL)
107-
updater.idle()
138+
parse_mode=ParseMode.HTML,
139+
)
140+
141+
async def reply(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
142+
if config.bot_replies_enabled():
143+
msg = update.message.text
144+
reply_spec = utils.triggers_reply(msg) if msg else None
145+
if reply_spec is not None:
146+
self.trace(f'bot sends reply {reply_spec.reply}')
147+
await context.bot.send_message(
148+
chat_id=update.message.chat_id,
149+
text=reply_spec.reply,
150+
parse_mode=ParseMode.HTML,
151+
)
152+
153+
def run(self):
154+
self.trace('Starting bot...')
155+
application = ApplicationBuilder().token(config.TELEGRAM_BOT_TOKEN).build()
156+
start_handler = CommandHandler('start', self.command_start)
157+
application.add_handler(start_handler)
158+
help_handler = CommandHandler('help', self.command_help)
159+
application.add_handler(help_handler)
160+
status_handler = CommandHandler('status', self.command_status)
161+
application.add_handler(status_handler)
162+
163+
# Zen Command
164+
application.add_handler(CommandHandler('zen', self.command_zen))
165+
166+
welcome_handler = MessageHandler(
167+
filters.StatusUpdate.NEW_CHAT_MEMBERS,
168+
self.welcome,
169+
)
170+
application.add_handler(welcome_handler)
171+
reply_handler = MessageHandler(
172+
filters.TEXT & (~filters.COMMAND),
173+
self.reply,
174+
)
175+
application.add_handler(reply_handler)
176+
self.trace('Bot is ready')
177+
application.run_polling(poll_interval=config.POLL_INTERVAL)
108178

109179

110180
if __name__ == "__main__":
111-
main()
181+
bot = DeckardBot()
182+
bot.run()

config.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ def log(logger_method):
4646
POLL_INTERVAL = config('POLL_INTERVAL', int, default=3)
4747

4848
# Bot message for start command
49-
BOT_GREETING = config('BOT_GREETING', default="Hi! I'm a friendly, slightly psychopath robot")
49+
BOT_GREETING = config(
50+
'BOT_GREETING',
51+
default="<b>Hi!</b> I'm a friendly, <s>crazy</s> slightly psychopath robot",
52+
)
5053

5154
# A username longer than this will be considered non-human
5255
# - Allowed values: An integer larger than 1
@@ -86,7 +89,7 @@ def bot_replies_enabled() -> bool:
8689
"There should be one-- and preferably only one --obvious way to do it.",
8790
"Although that way may not be obvious at first unless you're Dutch.",
8891
"Now is better than never.",
89-
"Although never is often better than *right* now.",
92+
"Although never is often better than <b>right</b> now.",
9093
"If the implementation is hard to explain, it's a bad idea.",
9194
"If the implementation is easy to explain, it may be a good idea.",
9295
"Namespaces are one honking great idea -- let's do more of those!",
@@ -97,7 +100,7 @@ def bot_replies_enabled() -> bool:
97100
("java",): "BIBA JABA!! ☕️",
98101
("cobol",): "BIBA KOBOL!! 💾",
99102
("javascript",): "BIBA JABAESKRIPT!! 🔮",
100-
("php",): "BIBA PEHACHEPÉ!! ⛱",
103+
("php",): "BIBA PEHACHEPÉ!.! ⛱",
101104
("chatgpt", "gpt", "openai"): "BIBA CHATJEPETÉ!! 🤖",
102105
(
103106
"he visto",

dev-requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ruff
2+
rope
3+
pytest

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
python-telegram-bot==13.8.1
2+
prettyconf==2.0.1

run.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
# Master script.
33

44
cd "$(dirname "$0")"
5-
exec pipenv run python bot.py
5+
source ~/.pyenv/versions/3.12.4/envs/pydeckard/bin/activate
6+
exec python bot.py

0 commit comments

Comments
 (0)