├── tabadol-ketab ├── .env.example ├── chromedriver_linux64.zip ├── requirements.txt ├── setting.py ├── bot.py └── models.py ├── .gitignore ├── Dockerfile └── README.md /tabadol-ketab/.env.example: -------------------------------------------------------------------------------- 1 | telegram_robot_access_token = 1234567891:ABCD_123456ABCD123456abcd1234567891 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .vscode/settings.json 3 | tabadol-ketab/__pycache__ 4 | tabadol-ketab/ghostdriver.log 5 | -------------------------------------------------------------------------------- /tabadol-ketab/chromedriver_linux64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Revisto/tabadol-ketab-bot/master/tabadol-ketab/chromedriver_linux64.zip -------------------------------------------------------------------------------- /tabadol-ketab/requirements.txt: -------------------------------------------------------------------------------- 1 | python-dotenv 2 | python-telegram-bot 3 | requests 4 | validator_collection 5 | bs4 6 | lxml 7 | persiantools 8 | humanize -------------------------------------------------------------------------------- /tabadol-ketab/setting.py: -------------------------------------------------------------------------------- 1 | import dotenv 2 | from os import getenv 3 | 4 | dotenv.load_dotenv() 5 | 6 | telegram_access_token = getenv("telegram_robot_access_token") -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | COPY tabadol-ketab /app 4 | WORKDIR /app 5 | 6 | RUN pip3 install -U setuptools 7 | RUN apt-get install -y libssl-dev libffi-dev 8 | RUN apt-get install -y libxml2-dev libxslt1-dev zlib1g-dev 9 | RUN pip3 install -r requirements.txt 10 | 11 | CMD ["python3","bot.py"] 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tabadol-Ketab Telegram Bot 2 | ## _A Telegram Bot that you can search a book inventory in [Tabadol-Ketab](https://tabadolketab.com/) with._ 3 | 4 | ![](https://revisto.ir/static/projects-cover/tabadol-ketab.jpg) 5 | 6 | Tabadol-Ketab-Telegram-Bot is a fast and user-friendly way telegram bot for checking Tabadol-Ketab book inventory. 7 | 8 | ## ✨ How To Use 9 | 10 | 1. Open your Telegram 11 | 2. Find this bot -> [@tabadol_ketab_bot](http://t.me/tabadol_ketab_bot) 12 | 3. Send your book name and get answers 13 | 4. Enjoy! 14 | 15 | ## ⚙️ Installation Your Own Telegram Bot 16 | 17 | tabadol-ketab-bot only and only requires [Docker](https://www.docker.com/) to be run. 18 | 19 | Install Docker and start the bot, docker takes care of other dependencies. 20 | 21 | ```sh 22 | apt install docker-ce 23 | ``` 24 | 25 | Now clone the repo: 26 | ```sh 27 | git clone https://github.com/Revisto/tabadol-ketab-bot 28 | cd tabadol-ketab-bot 29 | ``` 30 | 31 | Let's take care of .env files... 32 | 33 | ```sh 34 | cp tabadol-ketab/.env.example tabadol-ketab/.env 35 | ``` 36 | .env file contains your telegram bot access token. 37 | ``` 38 | telegram_robot_access_token = 1234567891:ABCD_123456ABCD123456abcd1234567891 39 | ``` 40 | 41 | ## Docker 42 | 43 | Make sure that you have done all installation steps and made .env files. 44 | then, build it and run it. 45 | ```sh 46 | docker build -t tk_bot . 47 | docker run -d tk_bot 48 | ``` 49 | 50 | ## 🤝 Contributing 51 | 52 | Contributions, issues and feature requests are welcome.
53 | Feel free to check [issues page](https://github.com/revisto/tabadol-ketab-bot/issues) if you want to contribute.

54 | 55 | 56 | ## Show your support 57 | 58 | Please ⭐️ this repository if this project helped you! 59 | 60 | 61 | ## 📝 License 62 | 63 | GNUv2 64 | 65 | **Free Software, Hell Yeah!** 66 | -------------------------------------------------------------------------------- /tabadol-ketab/bot.py: -------------------------------------------------------------------------------- 1 | from telegram.ext import CommandHandler, Filters, MessageHandler, Updater, ConversationHandler 2 | from validator_collection import is_not_empty 3 | 4 | import setting 5 | from models import TabadolKetab, Goodreads 6 | 7 | GET_USERNAME_GOODREADS = 0 8 | GET_BOOKS_NAMES = 0 9 | def search_a_book_by_only_name(update, context): 10 | update.message.reply_text(" بزار بگردم....") 11 | books = TabadolKetab().search_for_a_book(update.message.text, update) 12 | if not is_not_empty(books): 13 | update.message.reply_text("ای بابا. مثل اینکه این کتابو ندارن :(((((") 14 | 15 | def goodreads_search_multiple_books_intro(update, context): 16 | if context.user_data.get("goodreads_id") is not None: 17 | update.message.reply_text(f"""خب. حالا یوزرنیم Goodreadsیت رو بفرست یا. \n\n\n اگر میخوای با این یوزرنیم {context.user_data.get("goodreads_id")} پیش بری 1 رو بفرست. 18 | """) 19 | else: 20 | update.message.reply_text("""خب. حالا یوزرنیم Goodreadsیت رو بفرست. \n\n\n مثلا: 129432286-revisto 21 | """) 22 | return GET_USERNAME_GOODREADS 23 | 24 | def search_books_in_tabadol_ketab_intro(update, context): 25 | update.message.reply_text("""خب حالا اسم کتابایی که میخوای سرچ کنم رو برام بفرست و بین هر اسم یه اینتر بزن :) \n\n\n مثلا:\n\nهر روز\nناطوردشت\nشازده کوچولو\nسیزده دلیل برای این که 26 | """) 27 | return GET_BOOKS_NAMES 28 | 29 | def search_multiple_books(update, context): 30 | update.message.reply_text(" بزار بگردم....") 31 | books = update.message.text 32 | multiple_books_list = books.split("\n") 33 | TabadolKetab().search_for_books_and_send_book_names_immediately(multiple_books_list, update) 34 | return ConversationHandler.END 35 | 36 | def goodreads_books_in_tabadol_ketab_checker(update, context): 37 | update.message.reply_text("بزن بریم تو کارش. شاید یکمی طول بکشه...") 38 | goodreads_username = update.message.text 39 | if goodreads_username == "1" or goodreads_username == "۱": 40 | goodreads_username = context.user_data.get("goodreads_id") 41 | else: 42 | context.user_data["goodreads_id"] = goodreads_username 43 | goodreads_books = Goodreads().get_want_to_read_books_names(update.message.text) 44 | if not is_not_empty(goodreads_books): 45 | update.message.reply_text("یا یوزرنیمتو اشتباه وارد کردی, یا هیچ کتابی تو دسته want to readیت نداری :((((") 46 | return ConversationHandler.END 47 | update.message.reply_text("این کتابایی هستن که شما میخواین بخونین. تا دقایقی دیگه نتیجه رو بهتون میگم:") 48 | goodreads_books_message = "" 49 | for goodreads_book in goodreads_books: 50 | if len(goodreads_books_message) + len(goodreads_book) > 3800: 51 | update.message.reply_text(goodreads_books_message) 52 | goodreads_books_message = "" 53 | goodreads_books_message += f"{goodreads_book}\n" 54 | books = TabadolKetab().search_for_books_and_send_book_names_immediately(goodreads_books, update) 55 | if not is_not_empty(books): 56 | update.message.reply_text("ای بابا. مثل اینکه این کتابایی که تو Goodreadsیت هستنو ندارن :(") 57 | return ConversationHandler.END 58 | 59 | def start(update, context): 60 | update.message.reply_text("خب. سلااااام.\n\nبا این بات چیکارا میتونی بکنی؟ \nمیتونی اسم کتابو بفرستو و من بهت بگم که آیا توی تبادل موجود هست یا نه.\n\nمیتونی این کامند رو بزنی و بعد یوزرنیم گودریدزتو بدی بهم تا همه کتابای want to read گودریدزتو چک کنم و بهت بگم کدوما توی تبادل موجوده. /goodreadsbooksintabadol") 61 | 62 | def cancel(update, context): 63 | update.message.reply_text("حله") 64 | return ConversationHandler.END 65 | 66 | 67 | def main(): 68 | updater = Updater(setting.telegram_access_token, use_context=True) 69 | dp = updater.dispatcher 70 | 71 | goodreads_in_tabadolketab_converstation = ConversationHandler( 72 | entry_points=[CommandHandler('goodreadsbooksintabadol', goodreads_search_multiple_books_intro)], 73 | states={ 74 | GET_USERNAME_GOODREADS: [MessageHandler(Filters.text, goodreads_books_in_tabadol_ketab_checker)], 75 | }, 76 | fallbacks=[CommandHandler('cancel', cancel), CommandHandler('help', cancel), CommandHandler('start', cancel)], 77 | ) 78 | 79 | search_multiple_books_converstation = ConversationHandler( 80 | entry_points=[CommandHandler('searchmultiplebooks', search_books_in_tabadol_ketab_intro)], 81 | states={ 82 | GET_BOOKS_NAMES: [MessageHandler(Filters.text, search_multiple_books)], 83 | }, 84 | fallbacks=[CommandHandler('cancel', cancel), CommandHandler('help', cancel), CommandHandler('start', cancel)], 85 | ) 86 | 87 | dp.add_handler(goodreads_in_tabadolketab_converstation) 88 | dp.add_handler(search_multiple_books_converstation) 89 | 90 | dp.add_handler(CommandHandler("start", start)) 91 | dp.add_handler(CommandHandler("help", start)) 92 | dp.add_handler(MessageHandler(Filters.text, search_a_book_by_only_name)) 93 | 94 | updater.start_polling() 95 | updater.idle() 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | 101 | -------------------------------------------------------------------------------- /tabadol-ketab/models.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from validator_collection import is_not_empty 3 | from persiantools.jdatetime import JalaliDate 4 | from humanize import intcomma 5 | import xml.etree.ElementTree as ET 6 | import re 7 | 8 | MONTH_NAMES_FA = [ 9 | None, 10 | "فروردین", 11 | "اردیبهشت", 12 | "خرداد", 13 | "تیر", 14 | "مرداد", 15 | "شهریور", 16 | "مهر", 17 | "آبان", 18 | "آذر", 19 | "دی", 20 | "بهمن", 21 | "اسفند", 22 | ] 23 | 24 | class TabadolKetab: 25 | def __init__(self): 26 | self.tabadol_ketab_search_url = "https://book.tabadolketab.com/v1/api/tabaadol-e-ketaab/books?filter[name]={book_name}" 27 | 28 | def search_for_a_book(self, book_name, update): 29 | results = requests.get(self.tabadol_ketab_search_url.format(book_name=book_name)) 30 | results = results.json() 31 | books = [] 32 | for book_sum in results["result"]["docs"][:10]: 33 | book_request = requests.get("https://book.tabadolketab.com/v1/api/tabaadol-e-ketaab/book/" + book_sum["id"]).json() 34 | book_name = book_request.get("name") 35 | book_category = (book_request.get("category")).get("title") if book_request.get("category") is not None else "" 36 | book_author = (book_request.get("author")).get("title") if book_request.get("author") is not None else "" 37 | book_publisher = (book_request.get("publisher")).get("title") if book_request.get("publisher") is not None else "" 38 | book_translator = (book_request.get("translator")).get("title") if book_request.get("translator") is not None else "" 39 | book_price = intcomma(int(int(book_request.get("afterDiscount"))/10)) 40 | book_confirm_date = (book_request.get("confirmDate").split("T")[0]).split("-") 41 | try: 42 | book_confirm_date = str(JalaliDate.to_jalali(int(book_confirm_date[0]), int(book_confirm_date[1]), int(book_confirm_date[2]))).split("-") 43 | book_confirm_date_humanized = f"{book_confirm_date[2]} {MONTH_NAMES_FA[int(book_confirm_date[1])]} {book_confirm_date[0]}" 44 | except: 45 | book_confirm_date_humanized = "- - -" 46 | book_details = f""" 47 | نام کتاب: {book_name} 48 | 49 | 50 | دسته بندی: {book_category} 51 | نویسنده: {book_author} 52 | مترجم: {book_translator} 53 | ناشر: {book_publisher} 54 | قیمت: {book_price} 55 | تاریخ ورود: {book_confirm_date_humanized} 56 | """ 57 | update.message.reply_text(book_details) 58 | books.append({"book_name": book_name, "book_details": book_details}) 59 | 60 | return books 61 | 62 | def search_for_books(self, book_names): 63 | books = [] 64 | for book_name in book_names: 65 | results = requests.get(self.tabadol_ketab_search_url.format(book_name=book_name)) 66 | results = results.json() 67 | if is_not_empty(results["result"]["docs"]): 68 | books.append(book_name + " : " + results["result"]["docs"][0]["name"]) 69 | 70 | return books 71 | 72 | def search_for_books_and_send_book_names_immediately(self, book_names, update): 73 | books = [] 74 | for book_name in book_names: 75 | results = requests.get(self.tabadol_ketab_search_url.format(book_name=book_name)) 76 | results = results.json() 77 | if is_not_empty(results["result"]["docs"]): 78 | books.append(book_name + " : " + results["result"]["docs"][0]["name"]) 79 | telegram_bot(update).reply_text(books[-1]) 80 | 81 | return books 82 | 83 | 84 | 85 | class telegram_bot: 86 | def __init__(self, update): 87 | self.update = update 88 | 89 | def reply_text(self, message): 90 | self.update.message.reply_text(message) 91 | 92 | 93 | 94 | class Goodreads: 95 | 96 | def get_want_to_read_books_names(self, GOODREADS_USERID, GOODREADS_KEY='XlkrscdJCPAF0m9mjtwFtA'): 97 | all_titles = [] 98 | for page in range(1, 11): 99 | params = { 100 | 'v': '2', 101 | 'key': GOODREADS_KEY, 102 | 'id': GOODREADS_USERID, 103 | 'format': 'xml', 104 | 'shelf': 'to-read', 105 | 'per_page': '200', 106 | 'page': str(page) 107 | } 108 | resp = requests.get('https://www.goodreads.com/review/list/' + GOODREADS_USERID, params=params) 109 | root = ET.fromstring(resp.content) 110 | 111 | books = root.findall('./reviews/review/book') 112 | titles = [] 113 | for book_title in books: 114 | book_title = book_title.findall('./title')[0].text 115 | book_title = book_title.replace('\n', ' ').strip() 116 | book_title = book_title.replace('\u200c', ' ') 117 | book_title = book_title.replace('\u202b', '') 118 | book_title = re.sub("[\(\[].*?[\)\]]", "", book_title) 119 | book_title = re.sub(' +', ' ', book_title) 120 | for split_option in [":",";","؛",",","،"]: 121 | book_title = book_title.split(split_option)[0] 122 | titles.append(book_title) 123 | if titles == []: 124 | break 125 | 126 | all_titles.extend(titles) 127 | return all_titles --------------------------------------------------------------------------------