Skip to content

Commit ab52540

Browse files
committed
Redesign app, put commands into separate files
1 parent e713464 commit ab52540

12 files changed

+304
-136
lines changed

Procfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
worker: python bot.py
1+
worker: python main.py

README.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ Content-Length: 240
6868
## TODO
6969

7070
- Redesign commands:
71-
- /start
72-
- /poll - Notify user every 10 minutes what's the current price
73-
- /showlatestprice -> rename /price - Show current price, ask for address if not given already
74-
- /help
75-
- /stop
7671
- /setaddress(?) - User can set address so that bot doesn't need to ask it again in /start, /poll and /price commands
7772
- Show prices of all nearby restaurants
73+
- Don't ask address again in /start, /poll if it's found in the memory

bot.py

+12-124
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,38 @@
1-
import asyncio
2-
import i18n
31
import logging
42
import settings
53

64
from aiogram import Bot
75
from aiogram import Dispatcher
86
from aiogram.contrib.fsm_storage.memory import MemoryStorage
9-
from aiogram.dispatcher.filters.state import State
10-
from aiogram.dispatcher.filters.state import StatesGroup
117
from aiogram.utils import executor
128
from aiogram.types.message import ParseMode
139

14-
from scraper import fetch_delivery_price
15-
from utils import Message
16-
from utils import is_float
1710

18-
logging.basicConfig(level=logging.INFO)
11+
class TelegramBot:
1912

20-
# Initialize bot and dispatcher
21-
bot = Bot(settings.TOKEN, parse_mode=ParseMode.MARKDOWN_V2)
22-
storage = MemoryStorage()
23-
dp = Dispatcher(bot, storage=storage)
24-
25-
26-
# States for "start" command
27-
class Form(StatesGroup):
28-
address = State()
29-
max_price = State()
30-
31-
32-
async def _poll_price(message, data):
33-
address = data['address']
34-
max_price = data['max_price']
35-
state = None
36-
# Poll two hours at max
37-
for _ in range(20):
38-
result = await asyncio.gather(fetch_delivery_price(address))
39-
state = dp.current_state()
40-
data = await state.get_data()
41-
if not data['poll_price']:
42-
break
43-
price = result[0]
44-
if not price:
45-
await Message.answer(
46-
message, i18n['poll_failure'].format(
47-
address=address
48-
),
49-
escape_text=False, bot=bot
50-
)
51-
break
52-
price_str = format(price, '.2f')
53-
if price < max_price:
54-
await Message.answer(
55-
message, i18n['poll_success'].format(
56-
price_str=price_str
57-
),
58-
bot=bot
59-
)
60-
break
61-
await state.update_data(latest_price=price_str)
62-
await asyncio.sleep(60 * 10)
63-
await state.reset_state()
64-
65-
66-
@dp.message_handler(commands=['start'])
67-
async def cmd_start(message):
68-
await Form.address.set()
69-
return await Message.reply(message, i18n['start'])
70-
71-
72-
@dp.message_handler(state=Form.address)
73-
async def process_address(message, state):
74-
await state.update_data(address=message.text)
75-
await Form.next()
76-
return await Message.reply(message, i18n['process_address'])
77-
78-
79-
@dp.message_handler(lambda message: not is_float(message.text), state=Form.max_price)
80-
async def process_max_price_invalid(message):
81-
return await Message.reply(message, i18n['process_max_price_invalid'])
82-
83-
84-
@dp.message_handler(lambda message: is_float(message.text), state=Form.max_price)
85-
async def process_max_price(message, state):
86-
await state.update_data(
87-
max_price=float(message.text.replace(',', '.')),
88-
poll_interval=60 * 10, # seconds
89-
poll_price=True,
90-
latest_price=None
91-
)
92-
await Form.next()
93-
async with state.proxy() as data:
94-
asyncio.create_task(_poll_price(message, data))
95-
price_str = str(data['max_price']).replace('.', '\\.')
96-
return await Message.answer(
97-
message, i18n['process_max_price'].format(
98-
price_str=price_str,
99-
address=data['address']
100-
),
101-
escape_text=False
102-
)
103-
104-
105-
@dp.message_handler(commands=['showlatestprice'])
106-
async def cmd_latest_price(message):
107-
data = await dp.current_state().get_data()
108-
price = data.get('latest_price')
109-
if not price:
110-
return await Message.answer(message, i18n['latest_price_invalid'])
111-
else:
112-
return await Message.answer(message, i18n['latest_price'].format(price=price))
113-
114-
115-
@dp.message_handler(commands=['stop'])
116-
async def cmd_stop(message):
117-
await dp.current_state().update_data(poll_price=False)
118-
return await Message.answer(message, i18n['stop'])
119-
120-
121-
@dp.message_handler(commands=['help'])
122-
async def cmd_help(message):
123-
return await Message.answer(message, i18n['help'])
124-
125-
126-
@dp.message_handler()
127-
async def echo(message):
128-
print(message)
13+
# Initialize bot and dispatcher
14+
bot = Bot(settings.TOKEN, parse_mode=ParseMode.MARKDOWN_V2)
15+
storage = MemoryStorage()
16+
dp = Dispatcher(bot, storage=storage)
12917

13018

13119
async def on_startup(dp):
20+
from cmds import setup_handlers
13221
if settings.ENV == 'production':
133-
await bot.set_webhook(settings.WEBHOOK_URL)
22+
await TelegramBot.bot.set_webhook(settings.WEBHOOK_URL)
23+
setup_handlers(dp)
13424

13525

13626
async def on_shutdown(dp):
13727
# Remove webhook (not acceptable in some cases)
138-
await bot.delete_webhook()
28+
await TelegramBot.bot.delete_webhook()
13929

14030

14131
def main():
32+
logging.basicConfig(level=logging.INFO)
14233
if settings.MODE == 'webhook':
14334
executor.start_webhook(
144-
dispatcher=dp,
35+
dispatcher=TelegramBot.dp,
14536
webhook_path=settings.WEBHOOK_PATH,
14637
on_startup=on_startup,
14738
on_shutdown=on_shutdown,
@@ -150,7 +41,4 @@ def main():
15041
port=settings.WEBAPP_PORT
15142
)
15243
if settings.MODE == 'polling':
153-
executor.start_polling(dp, skip_updates=True)
154-
155-
156-
main()
44+
executor.start_polling(TelegramBot.dp, on_startup=on_startup, skip_updates=True)

cmds/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from cmds.start import setup_start
2+
from cmds.poll import setup_poll
3+
from cmds.price import setup_price
4+
from cmds.help import setup_help
5+
from cmds.stop import setup_stop
6+
from cmds.echo import setup_echo
7+
8+
9+
def setup_handlers(dp):
10+
setup_start(dp)
11+
setup_poll(dp)
12+
setup_price(dp)
13+
setup_help(dp)
14+
setup_stop(dp)
15+
setup_echo(dp)

cmds/echo.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
# @dp.message_handler(commands=['stop'])
4+
async def cmd_echo(message):
5+
print(message)
6+
message.text = 'jee'
7+
print(message)
8+
9+
10+
def setup_echo(dp):
11+
dp.register_message_handler(cmd_echo)

cmds/help.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import i18n
2+
3+
from utils import Message
4+
5+
6+
# @dp.message_handler(commands=['stop'])
7+
async def cmd_help(message):
8+
return await Message.answer(message, i18n['help'])
9+
10+
11+
def setup_help(dp):
12+
dp.register_message_handler(cmd_help, commands=['help'])

cmds/poll.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import asyncio
2+
import i18n
3+
4+
from aiogram.dispatcher.filters.state import State
5+
from aiogram.dispatcher.filters.state import StatesGroup
6+
7+
from bot import TelegramBot
8+
from scraper import fetch_delivery_price
9+
from utils import Message
10+
11+
12+
class Form(StatesGroup):
13+
address = State()
14+
15+
16+
async def _poll_price(message, address):
17+
state = None
18+
# Poll two hours at max
19+
for _ in range(20):
20+
result = await asyncio.gather(fetch_delivery_price(address))
21+
state = TelegramBot.dp.current_state()
22+
data = await state.get_data()
23+
if not data['poll_price']:
24+
break
25+
price = result[0]
26+
if not price:
27+
await Message.answer(
28+
message, i18n['poll_failure'].format(
29+
address=address
30+
),
31+
escape_text=False, bot=TelegramBot.bot
32+
)
33+
break
34+
price_str = format(price, '.2f')
35+
await Message.answer(
36+
message, i18n['latest_price'].format(price=price_str),
37+
bot=TelegramBot.bot
38+
)
39+
await state.update_data(latest_price=price_str)
40+
await asyncio.sleep(60 * 10)
41+
await state.reset_state()
42+
43+
44+
# @dp.message_handler(commands=['poll'])
45+
async def cmd_poll(message):
46+
await Form.address.set()
47+
return await Message.reply(message, i18n['start'])
48+
49+
50+
# @dp.message_handler(state=Form.address)
51+
async def process_address(message, state):
52+
await state.update_data(
53+
address=message.text,
54+
poll_price=True
55+
)
56+
await Form.next()
57+
async with state.proxy() as data:
58+
asyncio.create_task(_poll_price(message, data['address']))
59+
return await Message.answer(
60+
message, 'Haetaan...'
61+
)
62+
63+
64+
def setup_poll(dp):
65+
dp.register_message_handler(cmd_poll, commands=['poll'])
66+
dp.register_message_handler(process_address, state=Form.address)

cmds/price.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import asyncio
2+
import i18n
3+
4+
from aiogram.dispatcher.filters.state import State
5+
from aiogram.dispatcher.filters.state import StatesGroup
6+
7+
from bot import TelegramBot
8+
from scraper import fetch_delivery_price
9+
from utils import Message
10+
11+
12+
class PriceForm(StatesGroup):
13+
address = State()
14+
15+
16+
async def _get_price(message, address):
17+
result = await asyncio.gather(fetch_delivery_price(address))
18+
price = result[0]
19+
if not price:
20+
await Message.answer(
21+
message, i18n['poll_failure'].format(
22+
address=address
23+
),
24+
escape_text=False, bot=TelegramBot.bot
25+
)
26+
return
27+
price_str = format(price, '.2f')
28+
return await Message.answer(
29+
message, i18n['latest_price'].format(price=price_str),
30+
bot=TelegramBot.bot
31+
)
32+
33+
34+
# @dp.message_handler(commands=['poll'])
35+
async def cmd_price(message):
36+
data = await TelegramBot.dp.current_state().get_data()
37+
await PriceForm.address.set()
38+
if data.get('address'):
39+
message.text = data['address']
40+
state = TelegramBot.dp.current_state()
41+
return await process_address(message, state)
42+
return await Message.reply(message, i18n['start'])
43+
44+
45+
# @dp.message_handler(state=Form.address)
46+
async def process_address(message, state):
47+
await state.update_data(address=message.text)
48+
await PriceForm.next()
49+
async with state.proxy() as data:
50+
asyncio.create_task(_get_price(message, data['address']))
51+
return await Message.answer(
52+
message, 'Haetaan...'
53+
)
54+
55+
56+
def setup_price(dp):
57+
dp.register_message_handler(cmd_price, commands=['price'])
58+
dp.register_message_handler(process_address, state=PriceForm.address)

0 commit comments

Comments
 (0)