├── 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 | 
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
--------------------------------------------------------------------------------