bot.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. from aiogram import Bot, Dispatcher, executor, types
  2. from aiogram.dispatcher.handler import CancelHandler, current_handler
  3. from aiogram.dispatcher.middlewares import BaseMiddleware
  4. import requests
  5. from config import TOKEN, ADMIN_ID
  6. import dbworker as db
  7. import markups
  8. import pickle
  9. # initialize bot
  10. bot = Bot(TOKEN)
  11. dp = Dispatcher(bot)
  12. class BlockedUser(BaseMiddleware):
  13. """
  14. Middleware for a blocked user
  15. """
  16. def __init__(self):
  17. super(BlockedUser, self).__init__()
  18. async def on_process_message(self, message: types.Message, data: dict):
  19. if db.get_is_blocked(message.chat.id):
  20. await message.answer(
  21. "Вы заблокированы. По всем вопросам пишите моему создателю @nacknime"
  22. )
  23. raise CancelHandler()
  24. # func for button "back"
  25. @dp.message_handler(lambda message: message.text == "Назад")
  26. async def back(message):
  27. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  28. subtopic = db.get_subtopic(message.chat.id)
  29. subsubtopic = db.get_subsubtopic(message.chat.id)
  30. state = db.get_current_state(message.chat.id)
  31. if state == 8 and subtopic == "None":
  32. db.set_state(str(state - 3), message.chat.id)
  33. markup, msg = data[str(state - 3)]
  34. elif state == 8 and subsubtopic == "None":
  35. db.set_state(str(state - 2), message.chat.id)
  36. markup, msg = data[str(state - 2)]
  37. elif state <= 1:
  38. db.set_state("1", message.chat.id)
  39. markup = markups.klas()
  40. msg = "Выбери клас"
  41. else:
  42. db.set_state(str(state - 1), message.chat.id)
  43. markup, msg = data[str(state - 1)]
  44. await message.answer(msg, reply_markup=markup)
  45. # help
  46. @dp.message_handler(commands="help")
  47. async def help(message):
  48. await message.answer(
  49. """
  50. Open Source бот, который покажет тебе решение для твоей домашки максимально быстро!
  51. GitHub: https://github.com/Maks4816/gdz_ukraine
  52. Приму все пожелания и идеи для доработки бота - @nacknime
  53. """,
  54. )
  55. # send some message to all users
  56. @dp.message_handler(commands="send_all")
  57. async def send_all(message):
  58. if message.chat.id == ADMIN_ID:
  59. text = message.text[10:]
  60. count = 0
  61. for user in db.get_all_users():
  62. print(user)
  63. try:
  64. await bot.send_message(user, text, parse_mode="Markdown")
  65. except Exception as e:
  66. print(e)
  67. else:
  68. count += 1
  69. await message.answer(f"Успешно отправлено {count} юзерам")
  70. @dp.message_handler(commands=["block", "unblock"])
  71. async def block_unblock(message: types.Message):
  72. if message.chat.id == ADMIN_ID:
  73. if message.get_command() == "/block":
  74. user_id = message.text[7:]
  75. db.set_is_blocked(user_id, True)
  76. await message.answer(f"Юзер {user_id} был заблокирован.")
  77. else:
  78. user_id = message.text[9:]
  79. db.set_is_blocked(user_id, False)
  80. await message.answer(f"Юзер {user_id} был разблокирован.")
  81. # /start - choice the grade
  82. @dp.message_handler(lambda message: message.text == "Главное меню")
  83. @dp.message_handler(commands=["start"])
  84. async def start(message):
  85. db.get_and_set_id(message.chat.id)
  86. markup = markups.klas()
  87. msg = await message.answer("Выбери клас", reply_markup=markup)
  88. db.set_state("1", message.chat.id) # set state to '1' for specific ID
  89. data = pickle.dumps({"1": [markup, msg.text]}) # convert to bytes for save in DB
  90. db.set_keyboard_and_msg(
  91. data, message.chat.id
  92. ) # save keyboard and msg in DB for 'back' function
  93. # choice subject
  94. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 1)
  95. async def subject(message):
  96. # check a message on correct input
  97. data = pickle.loads(
  98. db.get_keyboard_and_msg(message.chat.id)
  99. ) # get keyboard and msg from DB, convert back to dict with objects
  100. markup = data[str(db.get_current_state(message.chat.id))][
  101. 0
  102. ] # get only markup from list
  103. if message.text not in [
  104. j["text"] for i in markup.keyboard for j in i
  105. ]: # if message.text not in buttons text
  106. await message.answer("Нажми на кнопку!")
  107. return # next code will not be executed, return to start of this handler
  108. klas = message.text.split()[
  109. 0
  110. ] # '5 клас' -> split -> ['5', 'клас'] -> get 1st element -> '5'
  111. db.set_klas(klas, message.chat.id)
  112. markup = markups.subject(klas)
  113. msg = await message.answer("Выбери предмет", reply_markup=markup)
  114. db.set_state("2", message.chat.id)
  115. data["2"] = [
  116. markup,
  117. msg.text,
  118. ] # keyboard and msg added to dict with key '2' (step 2)
  119. db.set_keyboard_and_msg(
  120. pickle.dumps(data), message.chat.id
  121. ) # convert to bytes with pickle and set it to DB
  122. # choice author
  123. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 2)
  124. async def author(message):
  125. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  126. markup = data[str(db.get_current_state(message.chat.id))][0]
  127. if message.text[-1] == "…" and len(message.text) == 128:
  128. for i in [j["text"] for i in markup.keyboard for j in i]:
  129. if i.startswith(message.text[:-1]):
  130. message.text = i
  131. break
  132. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  133. await message.answer("Нажми на кнопку!")
  134. return
  135. subject = message.text
  136. db.set_subject(subject, message.chat.id) # set subject to DB
  137. klas = db.get_klas(message.chat.id) # get grade for markup
  138. markup = markups.author(klas, subject)
  139. msg = await message.answer("Выбери автора", reply_markup=markup)
  140. db.set_state("3", message.chat.id)
  141. data["3"] = [markup, msg.text]
  142. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  143. # choice type
  144. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 3)
  145. async def type(message):
  146. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  147. markup = data[str(db.get_current_state(message.chat.id))][0]
  148. if message.text[-1] == "…" and len(message.text) == 128:
  149. for i in [j["text"] for i in markup.keyboard for j in i]:
  150. if i.startswith(message.text[:-1]):
  151. message.text = i
  152. break
  153. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  154. await message.answer("Нажми на кнопку!")
  155. return
  156. author = message.text
  157. db.set_author(author, message.chat.id)
  158. klas = db.get_klas(message.chat.id)
  159. subject = db.get_subject(message.chat.id)
  160. markup = markups.type(klas, subject, author)
  161. msg = await message.answer("Выбери тип", reply_markup=markup)
  162. db.set_state("4", message.chat.id)
  163. data["4"] = [markup, msg.text]
  164. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  165. # choice maintopic
  166. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 4)
  167. async def maintopic(message):
  168. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  169. markup = data[str(db.get_current_state(message.chat.id))][0]
  170. if message.text[-1] == "…" and len(message.text) == 128:
  171. for i in [j["text"] for i in markup.keyboard for j in i]:
  172. if i.startswith(message.text[:-1]):
  173. message.text = i
  174. break
  175. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  176. await message.answer("Нажми на кнопку!")
  177. return
  178. type = message.text
  179. db.set_type(type, message.chat.id)
  180. klas = db.get_klas(message.chat.id)
  181. subject = db.get_subject(message.chat.id)
  182. author = db.get_author(message.chat.id)
  183. markup = markups.maintopic(klas, subject, author, type)
  184. msg = await message.answer("Выбери главную тему", reply_markup=markup)
  185. db.set_state("5", message.chat.id)
  186. data["5"] = [markup, msg.text]
  187. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  188. # choice subtopic
  189. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 5)
  190. async def subtopic(message):
  191. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  192. markup = data[str(db.get_current_state(message.chat.id))][0]
  193. if message.text[-1] == "…" and len(message.text) == 128:
  194. for i in [j["text"] for i in markup.keyboard for j in i]:
  195. if i.startswith(message.text[:-1]):
  196. message.text = i
  197. break
  198. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  199. await message.answer("Нажми на кнопку!")
  200. return
  201. maintopic = message.text
  202. db.set_maintopic(maintopic, message.chat.id)
  203. klas = db.get_klas(message.chat.id)
  204. subject = db.get_subject(message.chat.id)
  205. author = db.get_author(message.chat.id)
  206. type = db.get_type(message.chat.id)
  207. markup = markups.subtopic(klas, subject, author, type, maintopic)
  208. if (
  209. markup.keyboard[2][0]["text"] != "None"
  210. ): # if 1st button's text != 'None': continue
  211. msg = await message.answer(
  212. "Выбери подтему", reply_markup=markup
  213. ) # 'subtopic' may not be, so we do a check
  214. db.set_state("6", message.chat.id)
  215. data["6"] = [markup, msg.text]
  216. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  217. else: # else we set next params to 'None' and...
  218. db.set_subtopic("None", message.chat.id) # ... jump to choice exercise
  219. db.set_subsubtopic("None", message.chat.id)
  220. markup = markups.exercise(
  221. klas, subject, author, type, maintopic, subtopic="None", subsubtopic="None"
  222. )
  223. msg = await message.answer("Выбери задание", reply_markup=markup)
  224. db.set_state("8", message.chat.id)
  225. data["8"] = [markup, msg.text]
  226. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  227. # choice subsubtopic :)
  228. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 6)
  229. async def subsubtopic(message):
  230. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  231. markup = data[str(db.get_current_state(message.chat.id))][0]
  232. if message.text[-1] == "…" and len(message.text) == 128:
  233. for i in [j["text"] for i in markup.keyboard for j in i]:
  234. if i.startswith(message.text[:-1]):
  235. message.text = i
  236. break
  237. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  238. await message.answer("Нажми на кнопку!")
  239. return
  240. subtopic = message.text
  241. db.set_subtopic(subtopic, message.chat.id)
  242. klas = db.get_klas(message.chat.id)
  243. subject = db.get_subject(message.chat.id)
  244. author = db.get_author(message.chat.id)
  245. type = db.get_type(message.chat.id)
  246. maintopic = db.get_maintopic(message.chat.id)
  247. markup = markups.subsubtopic(klas, subject, author, type, maintopic, subtopic)
  248. if markup.keyboard[2][0]["text"] != "None":
  249. msg = await message.answer("Выбери подподтему", reply_markup=markup)
  250. db.set_state("7", message.chat.id)
  251. data["7"] = [markup, msg.text]
  252. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  253. else:
  254. db.set_subsubtopic("None", message.chat.id)
  255. markup = markups.exercise(
  256. klas, subject, author, type, maintopic, subtopic, subsubtopic="None"
  257. )
  258. msg = await message.answer("Выбери задание", reply_markup=markup)
  259. db.set_state("8", message.chat.id)
  260. data["8"] = [markup, msg.text]
  261. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  262. # choice exercise
  263. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 7)
  264. async def exercise(message):
  265. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  266. markup = data[str(db.get_current_state(message.chat.id))][0]
  267. if message.text[-1] == "…" and len(message.text) == 128:
  268. for i in [j["text"] for i in markup.keyboard for j in i]:
  269. if i.startswith(message.text[:-1]):
  270. message.text = i
  271. break
  272. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  273. await message.answer("Нажми на кнопку!")
  274. return
  275. subsubtopic = message.text
  276. db.set_subsubtopic(subsubtopic, message.chat.id)
  277. klas = db.get_klas(message.chat.id)
  278. subject = db.get_subject(message.chat.id)
  279. author = db.get_author(message.chat.id)
  280. type = db.get_type(message.chat.id)
  281. maintopic = db.get_maintopic(message.chat.id)
  282. subtopic = db.get_subtopic(message.chat.id)
  283. subsubtopic = db.get_subsubtopic(message.chat.id)
  284. markup = markups.exercise(
  285. klas, subject, author, type, maintopic, subtopic, subsubtopic
  286. )
  287. msg = await message.answer("Выбери задание", reply_markup=markup)
  288. db.set_state("8", message.chat.id)
  289. data["8"] = [markup, msg.text]
  290. db.set_keyboard_and_msg(pickle.dumps(data), message.chat.id)
  291. # get solution
  292. @dp.message_handler(lambda message: db.get_current_state(message.chat.id) == 8)
  293. async def solution(message):
  294. data = pickle.loads(db.get_keyboard_and_msg(message.chat.id))
  295. markup = data[str(db.get_current_state(message.chat.id))][0]
  296. if message.text[-1] == "…" and len(message.text) == 128:
  297. for i in [j["text"] for i in markup.keyboard for j in i]:
  298. if i.startswith(message.text[:-1]):
  299. message.text = i
  300. break
  301. if message.text not in [j["text"] for i in markup.keyboard for j in i]:
  302. await message.answer("Нажми на кнопку!")
  303. return
  304. exercise = message.text
  305. db.set_exercise(exercise, message.chat.id)
  306. klas = db.get_klas(message.chat.id)
  307. subject = db.get_subject(message.chat.id)
  308. author = db.get_author(message.chat.id)
  309. type = db.get_type(message.chat.id)
  310. maintopic = db.get_maintopic(message.chat.id)
  311. subtopic = db.get_subtopic(message.chat.id)
  312. subsubtopic = db.get_subsubtopic(message.chat.id)
  313. solution = db.get_solution(
  314. klas, subject, author, type, maintopic, subtopic, subsubtopic, exercise
  315. ) # may return link on photo or file_id if exists
  316. if solution.startswith("/"): # link starts with '/'
  317. r = requests.get("https://cdn.gdz4you.com" + solution).content
  318. try:
  319. img = (
  320. (await bot.send_photo(message.chat.id, r)).photo[-1].file_id
  321. ) # get photo id after send that
  322. except Exception as e:
  323. print(e)
  324. img = (
  325. await bot.send_document(
  326. message.chat.id, "https://cdn.gdz4you.com" + solution
  327. )
  328. ).document.file_id
  329. img = "big_" + img
  330. db.set_solution(
  331. klas, subject, author, type, maintopic, subtopic, subsubtopic, exercise, img
  332. ) # set file_id to 'gdz'
  333. elif solution.startswith("big_"):
  334. await bot.send_document(
  335. message.chat.id, solution[4:]
  336. ) # if get a file id: send it by file id
  337. else:
  338. await bot.send_photo(message.chat.id, solution)
  339. if __name__ == "__main__":
  340. dp.middleware.setup(BlockedUser())
  341. executor.start_polling(dp)