Skip to content

Commit 3f4b164

Browse files
authored
Merge pull request #2 from mairror/feat/add_prediction
feat: add prediction
2 parents 7673735 + 6315f24 commit 3f4b164

File tree

7 files changed

+210
-57
lines changed

7 files changed

+210
-57
lines changed

.isort.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[settings]
2+
known_third_party = commands,config,dotenv,errors,requests,ruamel,telegram,utils

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ repos:
3636
rev: v5.4.2
3737
hooks:
3838
- id: isort
39+
args: ["--profile", "black", "--filter-files"]
3940
- repo: https://github.com/asottile/pyupgrade
4041
rev: v2.7.2
4142
hooks:

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# telegram-bot
2-
Bot telegram
2+
Bot telegram
33

44
## Converting Anaconda environment to pip requirements
55

@@ -23,3 +23,4 @@ There isn't any framework to do unit test with our code, you can check it in thi
2323
- [Telegram Bot Testing Suite](https://github.com/python-telegram-bot/ptbtest)
2424
- [Telegram Bot Testing Suite RTD](https://ptbtestsuite.readthedocs.io/en/master/?badge=master)
2525
- https://core.telegram.org/bots/api
26+
- https://api.slack.com/reference/surfaces/formatting

src/commands.py

Lines changed: 125 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
from telegram import Update
2-
from utils.logging import telegram_bot
3-
4-
import telegram
5-
from telegram.ext import (
6-
CallbackContext
7-
)
8-
import requests
1+
import json
92
from typing import Dict
10-
from config.settings import API_URL, API_KEY, API_UPLOAD_PATH
11-
123

4+
import requests
5+
from config.settings import API_KEY, API_PREDICT_PATH, API_UPLOAD_PATH, API_URL
6+
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
7+
from telegram.ext import CallbackContext
8+
from utils.logging import telegram_bot
139

1410

1511
def send_photo_to_api(files: Dict):
@@ -24,93 +20,187 @@ def send_photo_to_api(files: Dict):
2420
"""
2521
data = {"source": "telegram"}
2622
headers = {"X-Api-Key": API_KEY}
27-
telegram_bot.debug(f"Sending the file object to the API.")
23+
telegram_bot.debug("Sending the file object to the API.")
2824
try:
29-
r = requests.post(API_URL + API_UPLOAD_PATH, files=files, data=data, headers=headers)
25+
r = requests.post(
26+
API_URL + API_UPLOAD_PATH, files=files, data=data, headers=headers
27+
)
3028
if r.status_code == 201:
3129
telegram_bot.info(f"File sucessfully uploaded: {r.text}")
3230
except Exception as e:
3331
telegram_bot.error(f"There is an error when send the file: {e}.")
3432

3533

36-
3734
def start(update: Update, context: CallbackContext) -> None:
3835
"""
3936
Name: start
4037
Description:
41-
Start callable function when /start command is used on the telegram bot.
38+
Start callable function when /start command is used on the telegram bot.
4239
Send a reply when using that command.
43-
Inputs:
40+
Inputs:
4441
:update: type(Update): update handler object.
4542
:context: type(CallbackContext).
4643
Outputs:
47-
None
44+
None
4845
"""
49-
context.bot.send_message(chat_id=update.effective_chat.id, text="Welcome to mairror! Do you need to guess your age?")
46+
context.bot.send_message(
47+
chat_id=update.effective_chat.id,
48+
text="Welcome to mairror! Do you need to guess your age?",
49+
)
50+
5051

5152
def text(update: Update, context: CallbackContext) -> None:
5253
"""
5354
Name: text
5455
Description:
55-
When a text message is sent in the telegram bot, the context send that message.
56-
Inputs:
56+
When a text message is sent in the telegram bot, the context send that message.
57+
Inputs:
5758
:update: type(Update): update handler object.
5859
:context: type(CallbackContext).
5960
Outputs:
60-
None
61+
None
6162
"""
62-
context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand you.")
63+
context.bot.send_message(
64+
chat_id=update.effective_chat.id, text="Sorry, I didn't understand you."
65+
)
66+
6367

6468
def unknown(update: Update, context: CallbackContext) -> None:
6569
"""
6670
Name: unknown
6771
Description:
68-
When an unknown command is used the bot is replied with this message.
69-
Inputs:
72+
When an unknown command is used the bot is replied with this message.
73+
Inputs:
7074
:update: type(Update): update handler object.
7175
:context: type(CallbackContext).
7276
Outputs:
73-
None
77+
None
7478
"""
75-
context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.")
79+
context.bot.send_message(
80+
chat_id=update.effective_chat.id,
81+
text="Sorry, I didn't understand that command.",
82+
)
7683

7784

7885
def help_command(update: Update, context: CallbackContext) -> None:
7986
"""
8087
Name: unknown
8188
Description:
82-
When used the /help command, you can get the command helper for the telegram bot.
83-
Inputs:
89+
When used the /help command, you can get the command helper for the telegram bot.
90+
Inputs:
8491
:update: type(Update): update handler object.
8592
:context: type(CallbackContext).
8693
Outputs:
87-
None
94+
None
8895
"""
89-
update.message.reply_text('''
96+
update.message.reply_text(
97+
"""
9098
<b>Mairror Bot:</b>
9199
92100
You can find <a href="https://github.com/mairror/telegram-bot/blob/main/README.md">here</a> this bot's documentation.
93101
Authors of this bot:
94102
95103
- <a href="https://www.linkedin.com/in/%E2%9C%85-borja-l-422666a9">Borja</a>
96104
- <a href="https://www.linkedin.com/in/aacecan">Alex</a>
97-
''', parse_mode="HTML", disable_web_page_preview=True)
105+
""",
106+
parse_mode="HTML",
107+
disable_web_page_preview=True,
108+
)
109+
110+
111+
def build_text_prediction(prediction: Dict):
112+
newline = "\n"
113+
text = f"""
114+
<b>PREDICTION:</b>\n
115+
{newline.join([f"Face {count+1}: (Age: {pred['age']}), (Gender: {pred['gender']})"
116+
for count, pred in enumerate(prediction["predictions"]) ])}
117+
"""
118+
return text
119+
120+
121+
def predict(image):
122+
data = {"image_id": image}
123+
print(data)
124+
headers = {"X-Api-Key": API_KEY}
125+
telegram_bot.debug(f"Predict image {image}.")
126+
try:
127+
r = requests.post(
128+
API_URL + API_PREDICT_PATH, data=json.dumps(data), headers=headers
129+
)
130+
if r.status_code == 200:
131+
telegram_bot.info(f"Sucessfully predicted: {r.text}")
132+
return build_text_prediction(json.loads(r.text))
133+
else:
134+
telegram_bot.error(f"Error querying the API: {r.text}.")
135+
return "There was an error predicting your image."
136+
except Exception as e:
137+
telegram_bot.error(f"There is an error when get tyhe prediction: {e}.")
138+
139+
140+
def keyboard():
141+
keyboard = [
142+
[
143+
InlineKeyboardButton("Yes!!!", callback_data="yes"),
144+
InlineKeyboardButton("No", callback_data="no"),
145+
],
146+
]
147+
148+
reply_markup = InlineKeyboardMarkup(keyboard)
149+
return reply_markup
150+
151+
152+
def button(update: Update, context: CallbackContext) -> None:
153+
"""
154+
Name: photo
155+
Description:
156+
Used to send a photo into the API when you uploaded in the telegram bot.
157+
Inputs:
158+
:update: type(Update): update handler object.
159+
:context: type(CallbackContext).
160+
Outputs:
161+
None
162+
"""
163+
query = update.callback_query
164+
query.answer()
165+
if query.data == "yes":
166+
query.edit_message_text(text="I'm a great guesser!")
167+
else:
168+
query.edit_message_text(
169+
text="Sorry, I'll do the best of me. I need more images to improve the prediction."
170+
)
98171

99172

100173
def photo(update: Update, context: CallbackContext) -> None:
101174
"""
102175
Name: photo
103176
Description:
104-
Used to send a photo into the API when you uploaded in the telegram bot.
105-
Inputs:
177+
Used to send a photo into the API when you uploaded in the telegram bot.
178+
Inputs:
106179
:update: type(Update): update handler object.
107180
:context: type(CallbackContext).
108181
Outputs:
109-
None
182+
None
110183
"""
184+
image = f"{update.message.photo[-1].file_unique_id}_{update.message.chat.id}.jpg"
111185
files = {
112186
"file": (
113187
# AQADFrkxG-3bCVFy_3235057.jpg
114-
f"{update.message.photo[-1].file_unique_id}_{update.message.chat.id}.jpg",
115-
bytes(update.message.photo[-1].get_file().download_as_bytearray()))}
188+
image,
189+
bytes(update.message.photo[-1].get_file().download_as_bytearray()),
190+
)
191+
}
192+
116193
send_photo_to_api(files)
194+
195+
context.bot.send_chat_action(chat_id=update.effective_chat.id, action="typing")
196+
197+
prediction_result = predict("raw/" + image)
198+
if prediction_result:
199+
context.bot.send_message(
200+
chat_id=update.effective_chat.id,
201+
text=prediction_result,
202+
parse_mode="HTML",
203+
disable_web_page_preview=True,
204+
)
205+
reply_markup = keyboard()
206+
update.message.reply_text("Have I guessed your age?", reply_markup=reply_markup)

src/config/settings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from dotenv import load_dotenv
21
import os
32

3+
from dotenv import load_dotenv
44

55
if os.path.exists(".env"):
66
load_dotenv()
@@ -10,3 +10,5 @@
1010
API_URL = os.getenv("API_URL", "http://localhost:8000")
1111
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
1212
API_UPLOAD_PATH = os.getenv("API_UPLOAD_PATH", "/images/upload")
13+
API_PREDICT_PATH = os.getenv("API_PREDICT_PATH", "/images/predict")
14+
SLACK_WEBHOOK_URI = os.getenv("SLACK_WEBHOOK_URI", "xxxxx")

src/errors.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
import traceback
3+
4+
import requests
5+
from config.settings import SLACK_WEBHOOK_URI
6+
from telegram import Update
7+
from telegram.ext import CallbackContext
8+
from utils.logging import telegram_bot
9+
10+
11+
def error_handler(update: object, context: CallbackContext) -> None:
12+
"""
13+
Name: error_handler
14+
Description:
15+
Manage any error from the dispatcher logging it and send it to the developer chat id.
16+
Inputs:
17+
:update: type(Update): update handler object.
18+
:context: type(CallbackContext).
19+
Outputs:
20+
None
21+
"""
22+
telegram_bot.error(
23+
msg="Exception while handling an update:", exc_info=context.error
24+
)
25+
26+
tb_list = traceback.format_exception(
27+
None, context.error, context.error.__traceback__
28+
)
29+
tb_string = "".join(tb_list)
30+
31+
update_str = update.to_dict() if isinstance(update, Update) else str(update)
32+
message = f"""
33+
<!here>
34+
An exception was raised while handling an update:\n
35+
*UPDATE JSON DATA*
36+
```
37+
{json.dumps(update_str, indent=2, ensure_ascii=False)}
38+
```
39+
*TRACEBACK*
40+
```
41+
{tb_string}
42+
```
43+
"""
44+
45+
data = {
46+
"blocks": [{"type": "section", "text": {"type": "mrkdwn", "text": message}}]
47+
}
48+
49+
headers = {"Content-type": "application/json"}
50+
51+
requests.post(SLACK_WEBHOOK_URI, data=json.dumps(data), headers=headers)

src/main.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
from commands import button, help_command, photo, start, text, unknown
2+
from config.settings import BOT_TELEGRAM_TOKEN
3+
from errors import error_handler
14
from telegram.ext import (
2-
Updater,
3-
CommandHandler,
4-
MessageHandler,
5-
Filters
5+
CallbackQueryHandler,
6+
CommandHandler,
7+
Filters,
8+
MessageHandler,
9+
Updater,
610
)
711
from utils.logging import telegram_bot
8-
from config.settings import BOT_TELEGRAM_TOKEN
9-
from commands import start, photo, text, help_command, unknown
10-
1112

1213

1314
def main():
@@ -22,30 +23,35 @@ def main():
2223
- Photos -> These are required.
2324
- Gifs, Videos, Text, etc -> Not allowed
2425
"""
25-
updater = Updater(
26-
BOT_TELEGRAM_TOKEN,
27-
use_context=True,
28-
workers=32)
26+
telegram_bot.info("Starting telegram bot.")
27+
updater = Updater(BOT_TELEGRAM_TOKEN, use_context=True, workers=32)
2928

3029
dispatcher = updater.dispatcher
31-
30+
dispatcher.add_error_handler(error_handler)
31+
3232
dispatcher.add_handler(CommandHandler("start", start))
33-
34-
dispatcher.add_handler(MessageHandler(Filters.photo & ~Filters.command, photo, run_async=True))
35-
36-
text_handler = MessageHandler(Filters.text & (~Filters.command), text, run_async=True)
33+
34+
dispatcher.add_handler(
35+
MessageHandler(Filters.photo & ~Filters.command, photo, run_async=True)
36+
)
37+
38+
text_handler = MessageHandler(
39+
Filters.text & (~Filters.command), text, run_async=True
40+
)
3741
dispatcher.add_handler(text_handler)
38-
42+
3943
dispatcher.add_handler(CommandHandler("help", help_command))
40-
44+
45+
updater.dispatcher.add_handler(CallbackQueryHandler(button))
46+
4147
unknown_handler = MessageHandler(Filters.command, unknown, run_async=True)
4248
dispatcher.add_handler(unknown_handler)
4349

50+
telegram_bot.info("Start polling.")
4451

4552
updater.start_polling()
4653
updater.idle()
4754

4855

49-
if __name__ == '__main__':
56+
if __name__ == "__main__":
5057
main()
51-

0 commit comments

Comments
 (0)