-
Notifications
You must be signed in to change notification settings - Fork 1
/
bot.py
374 lines (302 loc) · 15.8 KB
/
bot.py
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
import logging
import datetime
import pytz
import sqlalchemy
from sqlalchemy import create_engine, MetaData, Table, Column, inspect
from sqlalchemy.orm import Session
from telegram.constants import ParseMode
import scraper
import json
from telegram import Update, BotCommand, Bot, KeyboardButton, ReplyKeyboardMarkup
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
import os
from sql_alchemy.database_connect import BotUser, Base, BotAdmin
global engine
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
for log_name, log_obj in logging.Logger.manager.loggerDict.items():
if log_name == "httpx":
log_obj.disabled = True
def add_user(uid: int):
global engine
with Session(engine) as session:
if not session.query(BotUser).filter(BotUser.uid == uid).first():
session.add(BotUser(uid=uid))
session.commit()
def is_admin(uid: int):
global engine
with Session(engine) as session:
if session.query(BotAdmin).filter(BotAdmin.uid == uid).first():
return session.query(BotAdmin).filter(BotAdmin.uid == uid).first().is_admin
else:
return False
def grid():
def grid():
keyboard = [
[KeyboardButton('Start Dubai'), KeyboardButton('Stop Dubai')],
[KeyboardButton('Start Doc'), KeyboardButton('Stop Doc')],
[KeyboardButton('Stop'), KeyboardButton('HELP')],
[KeyboardButton('Autori'), KeyboardButton('FAQ')]
]
reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
return reply_markup
async def add_admin(update: Update, context: ContextTypes.DEFAULT_TYPE):
global engine
if not is_admin(update.effective_chat.id):
logging.log(logging.INFO, f"user: {update.effective_chat.id} tried to run 'add admin' without admin "
f"privileges")
await context.bot.send_message(chat_id=update.effective_chat.id,
text="non sei amministratore, azione segnalata")
else:
if update.message.text.split(" ")[1].isdecimal():
uid = int(update.message.text.split(" ")[1])
else:
await context.bot.send_message(chat_id=update.effective_chat.id,
text=f"user id errato")
return
with Session(engine) as session:
if not session.query(BotAdmin).filter(BotAdmin.uid == uid).first():
session.add(BotAdmin(uid=uid))
session.commit()
await context.bot.send_message(chat_id=update.effective_chat.id,
text=f"l'utente {uid} è ora amministratore")
logging.log(logging.INFO, f"user: {uid} is now admin")
else:
await context.bot.send_message(chat_id=update.effective_chat.id,
text=f"l'utente {uid} è già amministratore")
async def menu_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
if datetime.datetime.today().weekday() == 5 or datetime.datetime.today().weekday() == 6:
await context.bot.send_message(chat_id=update.effective_chat.id,
text="Oggi il ristorante è chiuso")
else:
await context.bot.send_message(chat_id=update.effective_chat.id,
text=scraper.get_menu(f"{update.message.text[1:]}")["text"],
parse_mode=ParseMode.HTML, disable_web_page_preview=True)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# db_connector.add_user(update.effective_user.id)
add_user(update.effective_user.id)
await context.bot.send_message(chat_id=update.effective_chat.id,
parse_mode=ParseMode.HTML,
text="<i>Benvenuto nel bot del ristorante Doc&Dubai</i>\n\nPuoi richiedere i menu "
"dei due ristoranti usando rispettivamente /doc e /dubai\n\n"
"Per iscriverti al menù giornaliero usa /start_doc o /start_dubai "
", riceverai il menù alle 11:30 ogni giorno\n\n"
"/help per mostrare questo messaggio")
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(chat_id=update.effective_chat.id,
parse_mode=ParseMode.HTML,
text="<i>Benvenuto nel bot del ristorante Doc&Dubai</i>\n\nPuoi richiedere i menu "
"dei due ristoranti usando rispettivamente /doc e /dubai\n\n"
"Per iscriverti al menù giornaliero usa /start_doc o /start_dubai "
", riceverai il menù alle 11:30 ogni giorno\n\n"
"/help per mostrare questo messaggio")
async def menu_command_callback(context: ContextTypes.DEFAULT_TYPE):
print(f"job: {context.job.name}")
job = context.job
await context.bot.send_message(chat_id=job.chat_id,
text=scraper.get_menu(f"{job.data}")["text"], parse_mode=ParseMode.HTML,
disable_web_page_preview=True)
async def subscription_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_message.chat_id
resturant = ""
if update.message.text[0] == "/":
resturant = update.message.text.split('_')[1]
elif update.message.text.lower().split()[1] == "dubai" or update.message.text.lower().split()[1] == "doc":
resturant = update.message.text.lower().split()[1]
text = (f"Iscrizione effettuata, riceverai il menù del {resturant} ogni giorno alle 11:30\n"
f"Per cancellare l'iscrizione scrivi /stop_{resturant}")
add_user(chat_id)
global engine
with Session(engine) as session:
user = session.query(BotUser).filter(BotUser.uid == chat_id).first()
if resturant == "doc":
if user.doc:
text = f"Sei già iscritto al menù del doc, per cancellare l' iscrizione scrivi /stop_doc"
user.doc = True
elif resturant == "dubai":
if user.dubai:
text = f"Sei già iscritto al menù del dubai, per cancellare l' iscrizione scrivi /stop_dubai"
user.dubai = True
session.commit()
await context.bot.send_message(chat_id=update.effective_chat.id,
text=text)
async def unsubscription_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_message.chat_id
resturant = ""
if update.message.text[0] == "/":
resturant = update.message.text.split('_')[1]
elif update.message.text.lower().split()[1] == "dubai" or update.message.text.lower().split()[1] == "doc":
resturant = update.message.text.lower().split()[1]
text = f"Iscrizione cancellata, non riceverai più il menù del {resturant}\n"
add_user(chat_id)
global engine
with Session(engine) as session:
user = session.query(BotUser).filter(BotUser.uid == chat_id).first()
if resturant == "doc":
if not user.doc:
text = f"Non sei iscritto al menù del doc"
user.doc = False
elif resturant == "dubai":
if not user.dubai:
text = f"Non sei iscritto al menù del dubai"
user.dubai = False
session.commit()
await context.bot.send_message(chat_id=update.effective_chat.id,
text=text)
async def unknown_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(chat_id=update.effective_chat.id,
text="Questo comando non esiste. Usa la tastiera personalizzata per aiutarti!")
async def unknown_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
text = update.message.text.lower()
if text == 'start dubai' or text == 'start doc':
await subscription_command(update, context)
elif text == 'stop dubai' or text == 'stop doc':
await unsubscription_command(update, context)
elif text == 'help':
await help_command(update, context)
# elif text == 'autori':
# comando autori
# elif text == 'stop':
# comando stop
# elif text == 'faq':
# comando FAQ
else:
keyboard = grid()
await context.bot.send_message(chat_id=update.effective_chat.id, reply_markup=keyboard,
text="Questo comando non esiste. Usa la tastiera personalizzata per aiutarti!")
async def print_subscribers(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not is_admin(update.effective_chat.id):
logging.log(logging.INFO, f"user: {update.effective_chat.id} tried to run 'print_subscribers' without admin "
f"privileges")
return
with Session(engine) as session:
users = session.query(BotUser).all()
doc = [user.uid for user in users if user.doc]
dubai = [user.uid for user in users if user.dubai]
message = (f"subscribers:\n\n"
f"doc: {len(doc)}\n"
f"dubai: {len(dubai)}\n"
f"total: {len(users)}\n\n")
for user in users:
message += f"{user.uid}: {'Doc' if user.doc else ''} {'Dubai' if user.dubai else ''}\n"
await context.bot.send_message(chat_id=update.effective_chat.id,
text=message)
async def download_menus(context: ContextTypes.DEFAULT_TYPE):
scraper.download_menu("doc")
scraper.download_menu("dubai")
async def send_menus(context: ContextTypes.DEFAULT_TYPE):
menu_doc = scraper.get_menu("doc")
menu_dubai = scraper.get_menu("dubai")
global engine
retry = 0
database_get_succeded = False
while not database_get_succeded and retry < 3:
try:
with Session(engine) as session:
users = session.query(BotUser).all()
doc = [user.uid for user in users if user.doc]
dubai = [user.uid for user in users if user.dubai]
database_get_succeded = True
except Exception as e:
print(f"error while getting users: {e}")
retry = retry + 1
for uid in doc:
await context.bot.send_message(chat_id=uid, text=menu_doc["text"], parse_mode=ParseMode.HTML)
for uid in dubai:
await context.bot.send_message(chat_id=uid, text=menu_dubai["text"], parse_mode=ParseMode.HTML)
def init_db():
global engine
if not inspect(engine).has_table("subscriptions"): # If table don't exist, Create.
metadata = MetaData()
# Create a table with the appropriate Columns
Table("subscriptions", metadata,
Column("uid", sqlalchemy.Integer, primary_key=True),
Column("dubai", sqlalchemy.Boolean, default=False),
Column("doc", sqlalchemy.Boolean, default=False))
# Implement the creation
metadata.create_all(engine)
async def load_commands(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not is_admin(update.effective_chat.id):
logging.log(logging.INFO,f"user: {update.effective_chat.id} tried to run 'load_commands' without admin "
f"privileges")
return
await context.bot.set_my_commands([
BotCommand("doc", "Stampa menù del giorno del doc"),
BotCommand("dubai", "Stampa menù del giorno del dubai"),
BotCommand("start_doc", "ricevi il menù del doc ogni giorno"),
BotCommand("start_dubai", "ricevi il menù del dubai ogni giorno"),
BotCommand("stop_doc", "non ricevere il menù del doc ogni giorno"),
BotCommand("stop_dubai", "non ricevere il menù del dubai ogni giorno"),
BotCommand("help", "mostra messaggio di aiuto")
])
await context.bot.send_message(chat_id=update.effective_chat.id, text="comandi aggiornati")
async def send_menus_wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE):
if is_admin(update.effective_chat.id):
await send_menus(context)
else:
logging.log(logging.INFO, f"user: {update.effective_chat.id} tried to run 'send_menus_wrapper' without admin "
f"privileges")
async def announce(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not is_admin(update.effective_chat.id):
logging.log(logging.INFO,f"user: {update.effective_chat.id} tried to run 'announce' without admin "
f"privileges")
return
global engine
retry = 0
database_get_succeded = False
while not database_get_succeded and retry < 3:
try:
with Session(engine) as session:
users = session.query(BotUser).all()
database_get_succeded = True
except Exception as e:
print(f"error while getting users: {e}")
retry = retry + 1
for user in users:
await context.bot.send_message(chat_id=user.uid, text="ANNUNCIO: " + update.message.text[10:])
if __name__ == '__main__':
if os.getenv("SECRETS") is None:
os.environ["SECRETS"] = "secrets.json"
if os.getenv("COOKIES") is None:
os.environ["COOKIES"] = "cookies.json"
with open(os.getenv("SECRETS"), "r") as file:
config = json.load(file)
if os.getenv("DOCKER") is None:
config["db_host"] = "localhost"
application = ApplicationBuilder().token(config['token']).build()
application.job_queue.run_daily(download_menus, days=(1, 2, 3, 4, 5),
time=datetime.time(hour=10, minute=00, second=00,
tzinfo=pytz.timezone('Europe/Rome')),
job_kwargs={"misfire_grace_time": None})
# Due to a bug Job_queue is skipping job if timezone is not provided for job.run_daily.
application.job_queue.run_daily(send_menus, days=(1, 2, 3, 4, 5),
time=datetime.time(hour=11, minute=30, second=00,
tzinfo=pytz.timezone('Europe/Rome')),
job_kwargs={"misfire_grace_time": None})
# https://docs.sqlalchemy.org/en/20/core/engines.html#creating-urls-programmatically
engine = create_engine(sqlalchemy.URL.create(
"mysql+pymysql",
username=config["db_username"],
password=config["db_password"],
host=config["db_host"],
database=config["database"],
))
Base.metadata.create_all(engine)
application.add_handler(CommandHandler('start', start))
application.add_handler(CommandHandler('help', help_command))
application.add_handler(CommandHandler('dubai', menu_command))
application.add_handler(CommandHandler('doc', menu_command))
application.add_handler(CommandHandler('send', send_menus_wrapper))
application.add_handler(CommandHandler('announce', announce))
application.add_handler(CommandHandler('start_doc', subscription_command))
application.add_handler(CommandHandler('start_dubai', subscription_command))
application.add_handler(CommandHandler('stop_doc', unsubscription_command))
application.add_handler(CommandHandler('stop_dubai', unsubscription_command))
application.add_handler(CommandHandler('subscribers', print_subscribers))
application.add_handler(CommandHandler('set_commands', load_commands))
application.add_handler(CommandHandler('add_admin', add_admin))
application.add_handler(MessageHandler(filters.COMMAND, unknown_command))
application.add_handler(MessageHandler(None, unknown_text))
application.run_polling(allowed_updates=Update.ALL_TYPES)