From efc1f5de360520505f32bc820ba892f37686b755 Mon Sep 17 00:00:00 2001 From: PinLin Date: Tue, 10 Oct 2017 23:43:24 +0800 Subject: [PATCH] pep8 --- access.py | 25 +++---- bot.py | 50 +++++++------ interface.py | 201 ++++++++++++++++++++++++++------------------------- 3 files changed, 141 insertions(+), 135 deletions(-) diff --git a/access.py b/access.py index 5675720..f851e40 100644 --- a/access.py +++ b/access.py @@ -3,27 +3,26 @@ # necessary modules import requests from bs4 import BeautifulSoup -from pprint import pprint # configurations import config -class kuser_api: +class KuserAPI: def __init__(self): - self.session = requests.Session() + self._session = requests.Session() # login kCOJ def login_kcoj(self, username, password): try: payload = {'name': username, 'passwd': password, 'rdoCourse': 1} - return self.session.post(config.URL + '/Login', data=payload, timeout=0.5) + return self._session.post(config.URL + '/Login', data=payload, timeout=0.5) except requests.exceptions.Timeout: return None # check online status def check_online(self): try: - response = self.session.get(config.URL + '/TopMenu', timeout=0.5) + response = self._session.get(config.URL + '/TopMenu', timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') return soup.find('a').get_text().strip() == '線上考試' except requests.exceptions.Timeout: @@ -33,7 +32,7 @@ class kuser_api: def list_questions(self): try: questions = {} - response = self.session.get(config.URL + '/HomeworkBoard', timeout=0.5) + response = self._session.get(config.URL + '/HomeworkBoard', timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') for tag in soup.find_all('tr'): if tag.find('a') == None: @@ -51,7 +50,7 @@ class kuser_api: # show the content of the question def show_question(self, number): try: - response = self.session.get(config.URL + '/showHomework', params={'hwId': number}, timeout=0.5) + response = self._session.get(config.URL + '/showHomework', params={'hwId': number}, timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') raw = soup.find('body').get_text().replace('繳交作業', '').strip() content = '' @@ -65,7 +64,7 @@ class kuser_api: def list_passers(self, number): try: passers = [] - response = self.session.get(config.URL + '/success.jsp', params={'HW_ID': number}, timeout=0.5) + response = self._session.get(config.URL + '/success.jsp', params={'HW_ID': number}, timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') for tr in soup.find_all('tr'): passer = tr.get_text().replace('\n', '').strip() @@ -79,7 +78,7 @@ class kuser_api: def list_results(self, number, username): try: results = [] - response = self.session.get(config.URL + '/CheckResult.jsp', params={'questionID': number, 'studentID': username}, timeout=0.5) + response = self._session.get(config.URL + '/CheckResult.jsp', params={'questionID': number, 'studentID': username}, timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') for tr in soup.find_all('tr'): td = tr.find('td') @@ -94,7 +93,7 @@ class kuser_api: try: payload = {'pass': password, 'submit': 'sumit'} - response = self.session.post(config.URL + '/changePasswd', data=payload, timeout=0.5) + response = self._session.post(config.URL + '/changePasswd', data=payload, timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') return str(soup.find('body')).split()[-2].strip() == 'Success' except requests.exceptions.Timeout: @@ -103,7 +102,7 @@ class kuser_api: # delete the answer of the question def delete_answer(self, number): try: - response = self.session.get(config.URL + '/delHw', params={'title': number}, timeout=0.5) + response = self._session.get(config.URL + '/delHw', params={'title': number}, timeout=0.5) soup = BeautifulSoup(response.text, 'html.parser') return soup.find('body').get_text().replace('\n', '').strip() == 'delete success' except requests.exceptions.Timeout: @@ -112,8 +111,8 @@ class kuser_api: # hand in a answer def upload_answer(self, number, file_path): try: - self.session.get(config.URL + '/upLoadHw', params={'hwId': number}, timeout=0.5) - response = self.session.post(config.URL + '/upLoadFile', + self._session.get(config.URL + '/upLoadHw', params={'hwId': number}, timeout=0.5) + response = self._session.post(config.URL + '/upLoadFile', data={'FileDesc': 'Send from kcoj_bot'}, files={'hwFile': open(file_path, 'rb')}, timeout=0.5) diff --git a/bot.py b/bot.py index 1adcc9c..b997d23 100644 --- a/bot.py +++ b/bot.py @@ -1,12 +1,16 @@ #! /usr/bin/env python3 # necessary modules -import os, time, json, requests, telepot +import os +import time +import json +import requests +import telepot from telepot.loop import MessageLoop from pprint import pprint # kCOJ API import access -from interface import kuser, bot +from interface import Kuser, bot # configurations import config @@ -23,7 +27,7 @@ def on_chat(msg): print() # create a user object - user = kuser(from_id) + user = Kuser(from_id) if str(from_id) in users: user = users[str(from_id)] else: @@ -55,17 +59,17 @@ def on_chat(msg): user.help_you() # first-time user - elif user.status == '第一次用': + elif user._status == '第一次用': if chat_type == 'private': user.new_user() # press password - elif user.status == '輸入學號': + elif user._status == '輸入學號': if chat_type == 'private': user.press_password(msg['text']) # login - elif user.status == '輸入密碼': + elif user._status == '輸入密碼': if chat_type == 'private': user.login_kcoj(msg['text']) @@ -87,39 +91,39 @@ def on_chat(msg): user.press_oldpassword() elif command[0] == '/logout' or command[0] == '登出🚪': - user = kuser(from_id) + user = Kuser(from_id) users[str(from_id)] = user user.logout_system() - elif (command[0] == '/delete' or command[0] == '刪除作業⚔️') and user.question != '題外': + elif (command[0] == '/delete' or command[0] == '刪除作業⚔️') and user._question != '題外': if user.check_online(chat_id, msg['message_id']) == True: user.delete_answer() - elif (command[0] == '/upload' or command[0] == '交作業📮') and user.question != '題外': + elif (command[0] == '/upload' or command[0] == '交作業📮') and user._question != '題外': if user.check_online(chat_id, msg['message_id']) == True: user.upload_answer() - elif (command[0] == '/result' or command[0] == '看結果☑️') and user.question != '題外': + elif (command[0] == '/result' or command[0] == '看結果☑️') and user._question != '題外': if user.check_online(chat_id, msg['message_id']) == True: user.list_results() - elif (command[0] == '/passer' or command[0] == '通過者🌐') and user.question != '題外': + elif (command[0] == '/passer' or command[0] == '通過者🌐') and user._question != '題外': if user.check_online(chat_id, msg['message_id']) == True: user.list_passers() - elif command[0] == '回題目📜' and user.question != '題外': + elif command[0] == '回題目📜' and user._question != '題外': if user.check_online(chat_id, msg['message_id']) == True: - user.display_question(user.question, chat_id) + user.display_question(user._question, chat_id) - elif user.status == '舊的密碼': + elif user._status == '舊的密碼': if user.check_online(chat_id, msg['message_id']) == True: user.press_newpassword(msg['text']) - elif user.status == '修改密碼': + elif user._status == '修改密碼': if user.check_online(chat_id, msg['message_id']) == True: user.change_password(msg['text']) - elif user.status == '上傳答案': + elif user._status == '上傳答案': if user.check_online(chat_id, msg['message_id']) == True: user.send_answer(msg['text'], '') @@ -128,7 +132,7 @@ def on_chat(msg): bot.sendMessage(chat_id, "(ˊ・ω・ˋ)") elif content_type == 'document': - if user.status == '上傳答案' or user.status == '查看題目': + if user._status == '上傳答案' or user._status == '查看題目': if user.check_online(chat_id, msg['message_id']) == True: if msg['document']['file_size'] > 167770000: user.fail_send() @@ -140,11 +144,11 @@ def backup_db(): for key in users.keys(): user = users[key] users_backup[key] = { - 'userid': user.userid, - 'username': user.username, - 'password': user.password, - 'status': user.status, - 'question': user.question + 'userid': user._userid, + 'username': user._username, + 'password': user._password, + 'status': user._status, + 'question': user._question } with open('users.json', 'w') as f: json.dump(users_backup, f, indent=' ') @@ -154,7 +158,7 @@ def restore_db(): users_restore = json.load(f) for key in users_restore.keys(): user = users_restore[key] - users[key] = kuser(user['userid'], user['username'], user['password'], user['status'], user['question']) + users[key] = Kuser(user['userid'], user['username'], user['password'], user['status'], user['question']) # restore restore_db() diff --git a/interface.py b/interface.py index 766a4fb..dfa49d9 100644 --- a/interface.py +++ b/interface.py @@ -1,111 +1,114 @@ #! /usr/bin/env python3 # necessary modules -import os, telepot +import os +import telepot from telepot.namedtuple import ReplyKeyboardMarkup, ReplyKeyboardRemove from random import choice # kCOJ API import access # configurations -import config, promote, external +import config +import promote +import external bot = telepot.Bot(config.TOKEN) -class kuser: - def __init__(self, uid, un='', pw='', st='第一次用', qu='題外'): - self.userid = uid - self.username = un - self.password = pw - self.status = st - self.question = qu - self.api = access.kuser_api() +class Kuser: + def __init__(self, userid, username='', password='', status='第一次用', question='題外'): + self._userid = userid + self._username = username + self._password = password + self._status = status + self._question = question + self._api = access.KuserAPI() def new_user(self): self.help_you() self.press_username() def press_username(self): - self.status = '輸入學號' - self.question = '題外' - bot.sendMessage(self.userid, "請輸入您的學號:", reply_markup=ReplyKeyboardRemove()) + self._status = '輸入學號' + self._question = '題外' + bot.sendMessage(self._userid, "請輸入您的學號:", reply_markup=ReplyKeyboardRemove()) def press_password(self, text): - self.status = '輸入密碼' - self.question = '題外' - self.username = text - bot.sendMessage(self.userid, "輸入完可刪除訊息以策安全!\n" + self._status = '輸入密碼' + self._question = '題外' + self._username = text + bot.sendMessage(self._userid, "輸入完可刪除訊息以策安全!\n" "請輸入您的密碼:", reply_markup=ReplyKeyboardRemove()) def press_oldpassword(self): - self.status = '舊的密碼' - self.question = '題外' - bot.sendMessage(self.userid, "請輸入要原本的舊密碼:", + self._status = '舊的密碼' + self._question = '題外' + bot.sendMessage(self._userid, "請輸入要原本的舊密碼:", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠"] ], resize_keyboard=True)) def press_newpassword(self, text): - if text != self.password: - self.status = '正常使用' - self.question = '題外' - bot.sendMessage(self.userid, "密碼錯誤!", + if text != self._password: + self._status = '正常使用' + self._question = '題外' + bot.sendMessage(self._userid, "密碼錯誤!", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠"] ], resize_keyboard=True)) else: - self.status = '修改密碼' - self.question = '題外' - bot.sendMessage(self.userid, "使用此功能請務必小心!\n" + self._status = '修改密碼' + self._question = '題外' + bot.sendMessage(self._userid, "使用此功能請務必小心!\n" "請輸入要設定的新密碼:", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠"] ], resize_keyboard=True)) def change_password(self, text): - self.status = '正常使用' - self.question = '題外' - self.password = text - bot.sendMessage(self.userid, "修改成功!" if self.api.change_password(self.password) == True else "修改失敗。", + self._status = '正常使用' + self._question = '題外' + self._password = text + bot.sendMessage(self._userid, "修改成功!" if self._api.change_password(self._password) == True else "修改失敗。", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠"] ], resize_keyboard=True)) def login_kcoj(self, text): - self.status = '正常使用' - self.question = '題外' - self.password = text - bot.sendMessage(self.userid, "登入中...", reply_markup=ReplyKeyboardRemove()) - if self.check_online(self.userid) == True: - self.display_main(self.userid) + self._status = '正常使用' + self._question = '題外' + self._password = text + bot.sendMessage(self._userid, "登入中...", reply_markup=ReplyKeyboardRemove()) + if self.check_online(self._userid) == True: + self.display_main(self._userid) def fail_login(self, chat_id, message_id): - self.status = '正常使用' - self.question = '題外' - if chat_id != self.userid: + self._status = '正常使用' + self._question = '題外' + if chat_id != self._userid: bot.sendMessage(chat_id, "登入失敗,請先私訊我重新登入 kCOJ", reply_to_message_id=message_id) - bot.sendMessage(self.userid, "哇...登入失敗,讓我們重新開始", reply_markup=ReplyKeyboardRemove()) + bot.sendMessage(self._userid, "哇...登入失敗,讓我們重新開始", reply_markup=ReplyKeyboardRemove()) self.press_username() def fail_connecting(self, chat_id, message_id): - self.status = '正常使用' - self.question = '題外' - if chat_id != self.userid: + self._status = '正常使用' + self._question = '題外' + if chat_id != self._userid: bot.sendMessage(chat_id, "kCOJ 離線中!請稍後再試", reply_to_message_id=message_id) else: - bot.sendMessage(self.userid, "kCOJ 離線中!請稍後再試", + bot.sendMessage(self._userid, "kCOJ 離線中!請稍後再試", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "幫助📚"] ], resize_keyboard=True)) def check_online(self, chat_id, message_id=''): - result = self.api.check_online() + result = self._api.check_online() if result == None: self.fail_connecting(chat_id, message_id) return False else: if result == False: - self.api.login_kcoj(self.username, self.password) - result = self.api.check_online() + self._api.login_kcoj(self._username, self._password) + result = self._api.check_online() if result == False: self.fail_login(chat_id, message_id) elif result == None: @@ -113,22 +116,22 @@ class kuser: return result == True def logout_system(self): - self.status = '正常使用' - self.question = '題外' - bot.sendMessage(self.userid, "您現在已經是登出的狀態。", reply_markup=ReplyKeyboardRemove()) + self._status = '正常使用' + self._question = '題外' + bot.sendMessage(self._userid, "您現在已經是登出的狀態。", reply_markup=ReplyKeyboardRemove()) self.press_username() def display_main(self, chat_id): - self.status = '正常使用' - self.question = '題外' - q_dict = self.api.list_questions() + self._status = '正常使用' + self._question = '題外' + q_dict = self._api.list_questions() q_str = '' for key in q_dict.keys(): if q_dict[key][1] == '期限未到': q_str += "📗" + key + " (DL: " + q_dict[key][0] + ")\n [[" + q_dict[key][2] + "]]" q_str += "⚠️" if q_dict[key][2] == '未繳' else "✅" q_str += " /question_" + key + "\n\n" - bot.sendMessage(chat_id, "💁 " + self.username + " " + config.NAME + "\n" + bot.sendMessage(chat_id, "💁 " + self._username + " " + config.NAME + "\n" "➖➖➖➖➖\n" "📝可繳交的作業\n\n" + q_str + \ "➖➖➖➖➖\n" + choice(promote.sentences), @@ -136,20 +139,20 @@ class kuser: reply_markup=ReplyKeyboardMarkup(keyboard=[ ["題庫📝"], ["登出🚪", "改密碼💱", "幫助📚"] - ], resize_keyboard=True) if chat_id == self.userid else ReplyKeyboardRemove(), + ], resize_keyboard=True) if chat_id == self._userid else ReplyKeyboardRemove(), disable_web_page_preview=True) def display_questions(self, chat_id): - self.status = '正常使用' - self.question = '題外' - q_dict = self.api.list_questions() + self._status = '正常使用' + self._question = '題外' + q_dict = self._api.list_questions() q_str = '' for key in q_dict.keys(): q_str += "📗" if q_dict[key][1] == '期限未到' else "📕" q_str += "" + key + " (DL: " + q_dict[key][0] + ")\n [[" + q_dict[key][2] + "]]" q_str += "⚠️" if q_dict[key][2] == '未繳' else "✅" q_str += " /question_" + key + "\n\n" - reply = bot.sendMessage(chat_id, "💁 " + self.username + " " + config.NAME + "\n" + reply = bot.sendMessage(chat_id, "💁 " + self._username + " " + config.NAME + "\n" "➖➖➖➖➖\n" "📝所有作業\n\n" + q_str + \ "➖➖➖➖➖\n" + choice(promote.sentences), @@ -157,21 +160,21 @@ class kuser: reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "更新🔃"], ["登出🚪", "改密碼💱", "幫助📚"] - ], resize_keyboard=True) if chat_id == self.userid else ReplyKeyboardRemove(), + ], resize_keyboard=True) if chat_id == self._userid else ReplyKeyboardRemove(), disable_web_page_preview=True) bot.sendMessage(chat_id, "點我到題庫頂", reply_to_message_id=reply['message_id']) def display_question(self, number, chat_id): - self.status = '查看題目' - self.question = number + self._status = '查看題目' + self._question = number if number in external.QUESTION: ext_q = True content = external.QUESTION[number] else: ext_q = False - content = '```\n' + self.api.show_question(number) + '\n```' - q = self.api.list_questions()[number] - q_str = "💁 *" + self.username + "* [" + config.NAME + "]\n" + content = '```\n' + self._api.show_question(number) + '\n```' + q = self._api.list_questions()[number] + q_str = "💁 *" + self._username + "* [" + config.NAME + "]\n" q_str += "➖➖➖➖➖\n" q_str += "📗" if q[1] == '期限未到' else "📕" q_str += "*" + number + "* (DL: " + q[0] + ")\n [[[" + q[2] + "]]]" @@ -182,13 +185,13 @@ class kuser: ["首頁🏠", "題庫📝"], ["交作業📮" if q[1] == '期限未到' else '', "看結果☑️" if q[2] == '已繳' else '', "通過者🌐"], ["登出🚪", "改密碼💱", "幫助📚"] - ], resize_keyboard=True) if chat_id == self.userid else ReplyKeyboardRemove()) + ], resize_keyboard=True) if chat_id == self._userid else ReplyKeyboardRemove()) if ext_q == False: bot.sendMessage(chat_id, "點我到題目頂", reply_to_message_id=reply['message_id']) def help_you(self): - self.question = '題外' - bot.sendMessage(self.userid, "這裡是 kC Online Judge Bot!\n" + self._question = '題外' + bot.sendMessage(self._userid, "這裡是 kC Online Judge Bot!\n" "可以簡稱 kCOJ Bot,目前定居於 [" + config.NAME + "]\n" "作用是讓大家可以方便的透過我使用郭老程設課的 Online Judge\n" "➡️[傳送門](" + config.URL + ")\n" @@ -209,89 +212,89 @@ class kuser: parse_mode='Markdown') def upload_answer(self): - self.status = '上傳答案' - q = self.api.list_questions()[self.question] - q_str = "💁 " + self.username + " " + config.NAME + "\n" + self._status = '上傳答案' + q = self._api.list_questions()[self._question] + q_str = "💁 " + self._username + " " + config.NAME + "\n" q_str += "➖➖➖➖➖\n" q_str += "📗" if q[1] == '期限未到' else "📕" - q_str += "" + self.question + " (DL: " + q[0] + ")\n [[" + q[2] + "]]" + q_str += "" + self._question + " (DL: " + q[0] + ")\n [[" + q[2] + "]]" q_str += "⚠️" if q[2] == '未繳' else "✅" - bot.sendMessage(self.userid, q_str + "\n\n現在請把你的程式碼讓我看看(請別超過 20 MB)\n" + bot.sendMessage(self._userid, q_str + "\n\n現在請把你的程式碼讓我看看(請別超過 20 MB)\n" "可以使用「文字訊息」或是「傳送檔案」的方式", parse_mode='HTML', reply_markup=ReplyKeyboardMarkup(keyboard=[ - ["刪除作業⚔️"] if self.api.list_questions()[self.question][2] == '已繳' else [], + ["刪除作業⚔️"] if self._api.list_questions()[self._question][2] == '已繳' else [], ["首頁🏠", "回題目📜"] ], resize_keyboard=True)) def send_answer(self, text, file_id): - self.status = '正常使用' + self._status = '正常使用' if text != '': - with open(self.username + self.question + '.c', 'w') as f: + with open(self._username + self._question + '.c', 'w') as f: f.write(text) else: - bot.download_file(file_id, self.username + self.question + '.c') - self.api.delete_answer(self.question) - if self.api.upload_answer(self.question, self.username + self.question + '.c') == True: - bot.sendMessage(self.userid, "上傳成功", + bot.download_file(file_id, self._username + self._question + '.c') + self._api.delete_answer(self._question) + if self._api.upload_answer(self._question, self._username + self._question + '.c') == True: + bot.sendMessage(self._userid, "上傳成功", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"], ["看結果☑️"], ["登出🚪", "改密碼💱", "幫助📚"] ], resize_keyboard=True)) else: - bot.sendMessage(self.userid, "上傳失敗", + bot.sendMessage(self._userid, "上傳失敗", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"], ["登出🚪", "改密碼💱", "幫助📚"] ], resize_keyboard=True)) - os.remove(self.username + self.question + '.c') + os.remove(self._username + self._question + '.c') def delete_answer(self): - bot.sendMessage(self.userid, "移除成功" if self.api.delete_answer(self.question) == True else "移除失敗", + bot.sendMessage(self._userid, "移除成功" if self._api.delete_answer(self._question) == True else "移除失敗", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"], ["登出🚪", "改密碼💱", "幫助📚"] ], resize_keyboard=True)) def fail_send(self): - self.status = '正常使用' - bot.sendMessage(self.userid, "檔案不能超過 20 MB!上傳失敗", + self._status = '正常使用' + bot.sendMessage(self._userid, "檔案不能超過 20 MB!上傳失敗", reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"], ["登出🚪", "改密碼💱", "幫助📚"] ], resize_keyboard=True)) def list_passers(self): - self.status = '正常使用' - q = self.api.list_questions()[self.question] - q_str = "💁 " + self.username + " " + config.NAME + "\n" + self._status = '正常使用' + q = self._api.list_questions()[self._question] + q_str = "💁 " + self._username + " " + config.NAME + "\n" q_str += "➖➖➖➖➖\n" q_str += "📗" if q[1] == '期限未到' else "📕" - q_str += "" + self.question + " (DL: " + q[0] + ")\n [[" + q[2] + "]]" + q_str += "" + self._question + " (DL: " + q[0] + ")\n [[" + q[2] + "]]" q_str += "⚠️" if q[2] == '未繳' else "✅" q_str += "\n" - for passer in self.api.list_passers(self.question): + for passer in self._api.list_passers(self._question): q_str += "\n" + passer - reply = bot.sendMessage(self.userid, q_str + "", + reply = bot.sendMessage(self._userid, q_str + "", parse_mode='HTML', reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"], ["登出🚪", "改密碼💱", "幫助📚"] ], resize_keyboard=True)) - bot.sendMessage(self.userid, "點我到名單頂", reply_to_message_id=reply['message_id']) + bot.sendMessage(self._userid, "點我到名單頂", reply_to_message_id=reply['message_id']) def list_results(self): - self.status = '正常使用' - q = self.api.list_questions()[self.question] - q_str = "💁 " + self.username + " " + config.NAME + "\n" + self._status = '正常使用' + q = self._api.list_questions()[self._question] + q_str = "💁 " + self._username + " " + config.NAME + "\n" q_str += "➖➖➖➖➖\n" q_str += "📗" if q[1] == '期限未到' else "📕" - q_str += "" + self.question + " (DL: " + q[0] + ")\n" - for result in self.api.list_results(self.question, self.username): + q_str += "" + self._question + " (DL: " + q[0] + ")\n" + for result in self._api.list_results(self._question, self._username): q_str += "\n測試編號 " + result[0] + ":" q_str += "✔️ " if result[1] == '通過測試' else "❌ " q_str += result[1] - bot.sendMessage(self.userid, q_str, + bot.sendMessage(self._userid, q_str, parse_mode='HTML', reply_markup=ReplyKeyboardMarkup(keyboard=[ ["首頁🏠", "回題目📜"],