-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbot.py
More file actions
154 lines (126 loc) · 5.49 KB
/
Copy pathbot.py
File metadata and controls
154 lines (126 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import os
import sys
import logging
from dotenv import load_dotenv
from telegram import ParseMode, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, Updater, CallbackQueryHandler
from data import loadTrainTimingData
from utils import cleanForMarkdown, getSimilarStations
try: # Only need to run this func in dev env
load_dotenv()
except: # Fail silently in prod
pass
TELEGRAM_TOKEN = os.environ["TELEGRAM_API"] # Load in Telegram bot token
TRAIN_TIME_DATA = loadTrainTimingData(
"train-timing.json"
) # Load data from the json file
logging.basicConfig(
stream=sys.stdout,
format="%(levelname)s [%(asctime)s] %(message)s",
level=logging.INFO,
)
log = logging.getLogger()
log.info("Starting bot...")
def command_queryStation(update, context):
log.info("Query from user %s", update.message.from_user.username)
log.info("Message content: [%s]", update.message.text)
queryParams = context.args
if len(queryParams) == 0:
update.message.reply_text("Please give me a station name!")
log.info("Ending since query is empty")
return
stationName = " ".join(queryParams)
# Handle the case where user input doesn't exactly match
if stationName not in TRAIN_TIME_DATA:
log.info('"%s" is not in list of station names', stationName)
similarity = getSimilarStations(stationName, TRAIN_TIME_DATA.values())
stationName = cleanForMarkdown(stationName)
topSimRatio = similarity[0]["ratio"]
topSimStation = similarity[0]["station"]
log.info(
"Top similarity is %s, station is %s", str(topSimRatio), topSimStation.name
)
if 0.7 <= topSimRatio < 0.9:
# Not that good, but good enough as the top suggestion
keyboard = [
[
InlineKeyboardButton("Yes", callback_data=topSimStation.name),
InlineKeyboardButton("No", callback_data="invalid"),
]
]
update.message.reply_text(
f"*Couldn't find {stationName}\!* Did you mean *_{topSimStation.getCleanedName()}_*?",
parse_mode=ParseMode.MARKDOWN_V2,
reply_markup=InlineKeyboardMarkup(keyboard),
)
return
elif topSimRatio < 0.7:
# Suggest the top few names
update.message.reply_text(
f"*Couldn't find {stationName}\!* Here's some suggestions:",
parse_mode=ParseMode.MARKDOWN_V2,
)
update.message.reply_text(
"\n".join(
[
x["station"].getEmojiedName()
+ " _\("
+ cleanForMarkdown(str(round(100 * x["ratio"], 2)))
+ "% similar\)_"
for x in similarity[:5]
]
), # replace . with \. for Markdown
parse_mode=ParseMode.MARKDOWN_V2,
)
return
# Only reach here if topSim['ratio'] is 0.9 or higher
# Good enough match, use this as stationName instead
stationName = topSimStation.getCleanedName()
log.info("Sending out info for %s", stationName)
sendTrainTimingInfo(stationName, update.message)
log.info("Query from %s completed", update.message.from_user.username)
def command_start(update, context):
log.info("Start request from %s", update.message.from_user.username)
startMsg = "*Hello\! I'm LastTrainBot\.* Just use `/check <station name>` and I'll tell you the last times for that station\."
update.message.reply_text(startMsg, parse_mode=ParseMode.MARKDOWN_V2)
log.info("Start request from %s completed", update.message.from_user.username)
def command_fun(update, context):
log.info("Fun request from %s", update.message.from_user.username)
queryParams = context.args
if len(queryParams) == 0:
update.message.reply_text("Ask a question!")
log.info("Ending since query is empty")
return
question = " ".join(queryParams)
log.info("Fun request from %s completed", update.message.from_user.username)
def button_callback(update, context):
query = update.callback_query
log.info("Callback is %s", query.data)
query.answer()
if query.data == "invalid":
query.edit_message_text(text="You can try searching again using /check.")
log.info("Callback completed")
else:
query.delete_message()
sendTrainTimingInfo(query.data, update.callback_query.message)
log.info("Callback completed")
def sendTrainTimingInfo(stationName, message):
lineInformation = TRAIN_TIME_DATA[stationName].getLineInformation()
timingsMessages = TRAIN_TIME_DATA[stationName].getFormattedTimings()
# Send the station name + line information, followed by timing info in a new message
message.reply_text(
f"*{stationName} Station*\n_{lineInformation}_",
parse_mode=ParseMode.MARKDOWN_V2,
)
for eachMsg in timingsMessages:
message.reply_text(eachMsg, parse_mode=ParseMode.MARKDOWN_V2)
updater = Updater(TELEGRAM_TOKEN)
dispatcher = updater.dispatcher
dispatcher.add_handler(CallbackQueryHandler(button_callback))
dispatcher.add_handler(CommandHandler("start", command_start)) # Start command
dispatcher.add_handler(
CommandHandler("check", command_queryStation)
) # Actual query command
dispatcher.add_handler(CommandHandler("ask", command_fun)) # Actual query command
log.info("Bot started")
updater.start_polling()