from __future__ import annotations import json import time from threading import Thread from typing import TYPE_CHECKING from FunPayAPI.types import LotShortcut if TYPE_CHECKING: from cardinal import Cardinal from FunPayAPI.updater.events import * from os.path import exists from tg_bot import CBT from telebot.types import InlineKeyboardMarkup as K, InlineKeyboardButton as B import telebot import logging from locales.localizer import Localizer import tg_bot.static_keyboards localizer = Localizer() _ = localizer.translate NAME = "Autodumping Plugin" VERSION = "0.0.7" DESCRIPTION = "Автоматически понижает цены в зависимости от цен на рынке." CREDITS = "@sidor0912" UUID = "186042c8-f735-4f01-a11a-a3945b68b442" SETTINGS_PAGE = True logger = logging.getLogger("FPC.autodumping_plugin") LOGGER_PREFIX = "[AUTODUMPING PLUGIN]" SETTINGS = { "users_good": [], "users_bad": [], "currency": "₽", "time": 10 } # todo изменить время LOTS = {} CBT_CHANGE_CURRENCY = "AD_change_curr" CBT_TEXT_CHANGE_LOT = "AD_ChangeLot" CBT_TEXT_EDIT = "AD_Edit" CBT_TEXT_DELETE = "AD_DELETE" def init(cardinal: Cardinal): if not cardinal.telegram: return tg = cardinal.telegram bot = tg.bot if exists("storage/plugins/autodumping_plugin.json"): with open("storage/plugins/autodumping_plugin.json", "r", encoding="utf-8") as f: global SETTINGS settings = json.loads(f.read()) SETTINGS.update(settings) def save_settings(): with open("storage/plugins/autodumping_plugin.json", "w", encoding="utf-8") as f: global SETTINGS f.write(json.dumps(SETTINGS, indent=4, ensure_ascii=False)) logger.info(f"{LOGGER_PREFIX} Настройки сохранены.") def save_lots(): with open("storage/plugins/autodumping_plugin_lots.json", "w", encoding="utf-8") as f: global LOTS f.write(json.dumps(LOTS, indent=4, ensure_ascii=False)) logger.info(f"{LOGGER_PREFIX} Настройки лотов сохранены.") if exists("storage/plugins/autodumping_plugin_lots.json"): with open("storage/plugins/autodumping_plugin_lots.json", "r", encoding="utf-8") as f: global LOTS LOTS = json.loads(f.read()) for i, l in LOTS.items(): if "1_currency" not in l: LOTS[i]["1_currency"] = False save_lots() def open_settings(call: telebot.types.CallbackQuery): global SETTINGS keyboard = K() keyboard.add(B(f"Валюта: {SETTINGS['currency']}", callback_data=f"{CBT_CHANGE_CURRENCY}:")) # todo обработка кнопки keyboard.add(B(f"Каждые {SETTINGS['time']} минут", url=f"t.me/FPC_pluginss")) for lot_id, data in sorted(LOTS.items(), key=lambda x: int(x[0])): on = data.get("on") kw = data.get("keywords").replace('\n', ' ')[:100].rsplit(" ", 1)[0] keyboard.add(B(f"({lot_id}) {'🟢' if on else '🔴'} {kw}", callback_data=f"{CBT_TEXT_CHANGE_LOT}:{lot_id}")) keyboard.add(B("➕ Добавить лот", callback_data=f"{CBT_TEXT_CHANGE_LOT}:0")) keyboard.add(B("◀ Назад", callback_data=f"{CBT.EDIT_PLUGIN}:{UUID}:0")) bot.edit_message_text("В данном разделе Вы можете настроить демпинг.", call.message.chat.id, call.message.id, reply_markup=keyboard) bot.answer_callback_query(call.id) def switch_currency(call: telebot.types.CallbackQuery): global SETTINGS currencies = ["₽", "$", "€"] * 2 SETTINGS["currency"] = currencies[currencies.index(SETTINGS["currency"]) + 1] save_settings() open_settings(call) def to_lot_mess(call: telebot.types.CallbackQuery): n = call.data.split(":")[-1] global LOTS if n == "0" and n not in LOTS.keys(): # todo LOTS.setdefault(n, {"on": False, "keywords": "Нужно настроить ключевые слова лота.", "min": 0, "max": 99999, "users_good": False, "users_bad": False, "stars": 5, "ignore_0_stars": False, "step": 5, "divider": 1, "1_currency": False }) save_lots() keyboard = K() keyboard.add(B(f"{'🟢 Включено' if LOTS[n]['on'] else '🔴 Выключено'}", callback_data=f"{CBT_TEXT_EDIT}:{n}:on")) keyboard.add(B(f"✏ Изменить ключевые слова", callback_data=f"{CBT_TEXT_EDIT}:{n}:keywords")) keyboard.add(B(f"✏ ID лота: {n}", callback_data=f"{CBT_TEXT_EDIT}:{n}:lot_id")) keyboard.add( B(f"{'🟢' if LOTS[n]['1_currency'] else '🔴'}" + f" Минимум 1 {SETTINGS['currency']} без учета комиссии", callback_data=f"{CBT_TEXT_EDIT}:{n}:1_currency")) keyboard.add(B(f"✏ Мин. цена: {LOTS[n].get('min')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:min")) keyboard.add(B(f"✏ Макс. цена: {LOTS[n].get('max')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:max")) keyboard.add(B(f"✏ Шаг: {LOTS[n].get('step')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:step")) keyboard.add(B(f"✏ Делитель: {LOTS[n].get('divider')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:divider")) keyboard.add(B(f"{'🟢' if LOTS[n].get('users_good') else '🔴'} Игнор лотов друзей", callback_data=f"{CBT_TEXT_EDIT}:{n}:users_good")) keyboard.add(B(f"{'🟢' if LOTS[n].get('users_bad') else '🔴'} Игнор всех, кроме конкурентов", callback_data=f"{CBT_TEXT_EDIT}:{n}:users_bad")) keyboard.add(B(f"{'🟢' if LOTS[n].get('ignore_0_stars') else '🔴'} Игнор челов без рейтинга", callback_data=f"{CBT_TEXT_EDIT}:{n}:ignore_0_stars")) keyboard.add(B(f"{LOTS[n].get('stars')}+ ⭐", callback_data=f"{CBT_TEXT_EDIT}:{n}:stars")) keyboard.add(B(f"🗑 Удалить", callback_data=f"{CBT_TEXT_DELETE}:{n}")) keyboard.add(B("◀ Назад", callback_data=f"{CBT.PLUGIN_SETTINGS}:{UUID}")) txt = f"https://Funpay.com/lots/offer?id={n}\n\n<b>Ключевые слова:</b> {LOTS.get(n).get('keywords')}" bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.id, text=txt, reply_markup=keyboard) def answer_to_lot_mess(call: telebot.types.CallbackQuery): *a, n, key = call.data.split(":") global LOTS if key in ("max", "min", "keywords", "lot_id", "step", "divider"): bot.answer_callback_query(call.id) d = { "max": "максимальную цену", "min": "минимальную цену", "keywords": "ключевые слова (<code>|</code> для разделения равнозначных значений)", "lot_id": "ID лота", "step": "шаг", "divider": "делитель" } text = f'Введите {d.get(key, "что-то")} для https://Funpay.com/lots/offer?id={n} :' msg = bot.send_message(call.message.chat.id, text=text, reply_markup=tg_bot.static_keyboards.CLEAR_STATE_BTN()) tg.set_state(call.message.chat.id, msg.id, call.from_user.id, CBT_TEXT_EDIT, data={"key": key, "n": n}) bot.answer_callback_query(call.id) return if key in ("on", "users_good", "users_bad", "ignore_0_stars", "1_currency"): LOTS[n][key] = not LOTS[n][key] elif key == "stars": LOTS[n][key] = LOTS[n][key] % 5 + 1 save_lots() call.data = f"{CBT_TEXT_CHANGE_LOT}:{n}" to_lot_mess(call) return def to_delete(call: telebot.types.CallbackQuery): n = call.data.split(":")[-1] global LOTS del LOTS[n] save_lots() open_settings(call) def edited(message: telebot.types.Message): global LOTS text = message.text data = tg.get_state(message.chat.id, message.from_user.id)["data"] if data.get("key") in ("max", "min", "keywords", "step", "divider"): if data.get("key") == "keywords": text = text.lower() else: text = float(text) LOTS[data.get("n")][data.get("key")] = text elif data.get("key") == "lot_id": text = str(int("".join([char for char in text if char.isdigit()]))) LOTS[text] = LOTS[data.get("n")] del LOTS[data.get("n")] tg.clear_state(message.chat.id, message.from_user.id, True) keyboard = K() \ .row(B("◀ Назад", callback_data=f"{CBT_TEXT_CHANGE_LOT}:{data.get('n') if data.get('key') != 'lot_id' else text}")) save_lots() bot.reply_to(message, f"✅", reply_markup=keyboard) def edit_users_list(message: telebot.types.Message): global SETTINGS text = message.text users = text.split(" ")[1:] if (text.startswith("/adl_good")): key = "users_good" elif (text.startswith("/adl_bad")): key = "users_bad" else: raise Exception() for user in users: if user not in SETTINGS[key]: SETTINGS[key].append(user) else: SETTINGS[key].remove(user) save_settings() bot.reply_to(message, f"{'Список друзей' if key == 'users_good' else 'Список конкурентов'}:\n{', '.join([f'<code>{i}</code>' for i in SETTINGS[key]])}") tg.msg_handler(edited, func=lambda m: tg.check_state(m.chat.id, m.from_user.id, CBT_TEXT_EDIT)) tg.msg_handler(edit_users_list, func=lambda m: m.text.startswith("/adl_")) tg.cbq_handler(to_lot_mess, lambda c: c.data.startswith(f"{CBT_TEXT_CHANGE_LOT}:")) tg.cbq_handler(switch_currency, lambda c: f"{CBT_CHANGE_CURRENCY}" in c.data) tg.cbq_handler(open_settings, lambda c: f"{CBT.PLUGIN_SETTINGS}:{UUID}" in c.data) tg.cbq_handler(answer_to_lot_mess, lambda c: c.data.startswith(f"{CBT_TEXT_EDIT}:")) tg.cbq_handler(to_delete, lambda c: c.data.startswith(f"{CBT_TEXT_DELETE}:")) cardinal.add_telegram_commands(UUID, [ (f"adl_good", "Друзья", True), (f"adl_bad", "Конкуренты", True), ]) def post_start(cardinal): def filter_lots(lot: LotShortcut, my_lot_id): global LOTS if lot.seller.username == cardinal.account.username: return False kw = LOTS.get(my_lot_id).get("keywords") if not all([any([j in lot.description.lower() for j in i.split("|")]) for i in kw.split("\n")]): return False if LOTS.get(my_lot_id).get("users_good") and lot.seller.username in SETTINGS.get("users_good"): return False if LOTS.get(my_lot_id).get("users_bad") and lot.seller.username not in SETTINGS.get("users_bad"): return False if not (LOTS.get(my_lot_id).get("min") + LOTS.get(my_lot_id).get("step") <= lot.price <= LOTS.get( my_lot_id).get("max") + LOTS.get(my_lot_id).get("step")): return False if lot.seller.stars is None and LOTS.get(my_lot_id).get("ignore_0_stars"): return False if lot.seller.stars is not None and lot.seller.stars < LOTS.get(my_lot_id).get("stars"): return False return True def get_commission(node_id): headers = { "accept": "*/*", "x-requested-with": "XMLHttpRequest" } data = { "nodeId": node_id, "price": 1000 } calc = cardinal.account.method("post", "https://Funpay.com/lots/calc", headers, data).json()["methods"] commission = float( min([i for i in calc if i.get("unit") == SETTINGS["currency"]], key=lambda x: float(x.get("price"))).get( "price")) / 1000 return commission def change_price(cardinal: Cardinal, my_lot_id: int, new_price: float, commission: float, min_1: bool): lot_fields = cardinal.account.get_lot_fields(my_lot_id) time.sleep(1) price_without_comission = new_price / commission if price_without_comission < 1 and min_1: price_without_comission = 1 old_price = lot_fields.price if r := abs(price_without_comission - old_price) >= 0.001: lot_fields.price = price_without_comission cardinal.account.save_lot(lot_fields) logger.info( f"{LOGGER_PREFIX} Сохранил лот $MAGENTA{my_lot_id}$RESET. Цена: {old_price} -> {price_without_comission}") else: logger.info( f"{LOGGER_PREFIX} Цена лота $MAGENTA{my_lot_id}$RESET не изменилась: {old_price} -> {price_without_comission}") def process(cardinal): global LOTS while True: logger.info(f"{LOGGER_PREFIX} Новый проход. {'-' * 100}") try: to_break = False result = cardinal.update_lots_and_categories() if not result: time.sleep(30) continue my_lots = cardinal.tg_profile.get_sorted_lots(2) for category, i in my_lots.items(): if category.type == SubCategoryTypes.CURRENCY or category.id == 210: continue category_lots = None category_comission = None for my_lot_id, lot in i.items(): if str(lot.currency) != SETTINGS.get("currency"): to_break = True break my_lot_id = str(my_lot_id) if str(my_lot_id) not in LOTS.keys() or not LOTS[my_lot_id].get("on"): continue logger.info(f"{LOGGER_PREFIX} Обрабатываю лот {my_lot_id}.") try: if category_lots is None: category_lots = cardinal.account.get_subcategory_public_lots(category.type, category.id) time.sleep(1) logger.info( f"{LOGGER_PREFIX} Получил {len(category_lots)} лотов категории $MAGENTA{category.fullname}$RESET.") if category_comission is None: category_comission = get_commission(category.id) time.sleep(1) logger.info( f"{LOGGER_PREFIX} Комиссия раздела {category.fullname} - {category_comission}.") category_lots_4_lot = list(filter(lambda x: filter_lots(x, my_lot_id), category_lots)) logger.info( f"{LOGGER_PREFIX} Осталось {len(category_lots_4_lot)} лотов для лота {my_lot_id} после фильрации.") min_1 = LOTS[my_lot_id].get("1_currency", False) if category_lots_4_lot: logger.info( f"{LOGGER_PREFIX} Список подходящих лотов: {', '.join([str(l.id) for l in category_lots_4_lot])}") min_price_lot = min(category_lots_4_lot, key=lambda x: x.price) if str(min_price_lot.currency) != SETTINGS.get("currency"): to_break = True break logger.info(f"{LOGGER_PREFIX} Перебиваем лот {min_price_lot.id} в лоте {my_lot_id}") first = min_price_lot.price - LOTS[my_lot_id].get("step") change_price(cardinal, my_lot_id, min(LOTS[my_lot_id].get("max"), first - first % LOTS[my_lot_id].get("divider")), category_comission, min_1) else: change_price(cardinal, my_lot_id, LOTS[my_lot_id].get("max"), category_comission, min_1) time.sleep(1) except: logger.warning( f"{LOGGER_PREFIX} Произошла ошибка при обработке лота {my_lot_id}. Лот пропущен.") logger.debug("TRACEBACK", exc_info=True) if to_break: break except: logger.warning( f"{LOGGER_PREFIX} Произошла ошибка. Итерация пропущена. Следующая попытка через {SETTINGS.get('time')} минут.") logger.debug("TRACEBACK", exc_info=True) time.sleep(max(SETTINGS.get("time", 0) * 60, 5)) Thread(target=process, daemon=True, args=(cardinal,)).start() BIND_TO_PRE_INIT = [init] BIND_TO_POST_START = [post_start] BIND_TO_DELETE = None Python from __future__ import annotations import json import time from threading import Thread from typing import TYPE_CHECKING from FunPayAPI.types import LotShortcut if TYPE_CHECKING: from cardinal import Cardinal from FunPayAPI.updater.events import * from os.path import exists from tg_bot import CBT from telebot.types import InlineKeyboardMarkup as K, InlineKeyboardButton as B import telebot import logging from locales.localizer import Localizer import tg_bot.static_keyboards localizer = Localizer() _ = localizer.translate NAME = "Autodumping Plugin" VERSION = "0.0.7" DESCRIPTION = "Автоматически понижает цены в зависимости от цен на рынке." CREDITS = "@sidor0912" UUID = "186042c8-f735-4f01-a11a-a3945b68b442" SETTINGS_PAGE = True logger = logging.getLogger("FPC.autodumping_plugin") LOGGER_PREFIX = "[AUTODUMPING PLUGIN]" SETTINGS = { "users_good": [], "users_bad": [], "currency": "₽", "time": 10 } # todo изменить время LOTS = {} CBT_CHANGE_CURRENCY = "AD_change_curr" CBT_TEXT_CHANGE_LOT = "AD_ChangeLot" CBT_TEXT_EDIT = "AD_Edit" CBT_TEXT_DELETE = "AD_DELETE" def init(cardinal: Cardinal): if not cardinal.telegram: return tg = cardinal.telegram bot = tg.bot if exists("storage/plugins/autodumping_plugin.json"): with open("storage/plugins/autodumping_plugin.json", "r", encoding="utf-8") as f: global SETTINGS settings = json.loads(f.read()) SETTINGS.update(settings) def save_settings(): with open("storage/plugins/autodumping_plugin.json", "w", encoding="utf-8") as f: global SETTINGS f.write(json.dumps(SETTINGS, indent=4, ensure_ascii=False)) logger.info(f"{LOGGER_PREFIX} Настройки сохранены.") def save_lots(): with open("storage/plugins/autodumping_plugin_lots.json", "w", encoding="utf-8") as f: global LOTS f.write(json.dumps(LOTS, indent=4, ensure_ascii=False)) logger.info(f"{LOGGER_PREFIX} Настройки лотов сохранены.") if exists("storage/plugins/autodumping_plugin_lots.json"): with open("storage/plugins/autodumping_plugin_lots.json", "r", encoding="utf-8") as f: global LOTS LOTS = json.loads(f.read()) for i, l in LOTS.items(): if "1_currency" not in l: LOTS[i]["1_currency"] = False save_lots() def open_settings(call: telebot.types.CallbackQuery): global SETTINGS keyboard = K() keyboard.add(B(f"Валюта: {SETTINGS['currency']}", callback_data=f"{CBT_CHANGE_CURRENCY}:")) # todo обработка кнопки keyboard.add(B(f"Каждые {SETTINGS['time']} минут", url=f"t.me/FPC_pluginss")) for lot_id, data in sorted(LOTS.items(), key=lambda x: int(x[0])): on = data.get("on") kw = data.get("keywords").replace('\n', ' ')[:100].rsplit(" ", 1)[0] keyboard.add(B(f"({lot_id}) {'🟢' if on else '🔴'} {kw}", callback_data=f"{CBT_TEXT_CHANGE_LOT}:{lot_id}")) keyboard.add(B("➕ Добавить лот", callback_data=f"{CBT_TEXT_CHANGE_LOT}:0")) keyboard.add(B("◀ Назад", callback_data=f"{CBT.EDIT_PLUGIN}:{UUID}:0")) bot.edit_message_text("В данном разделе Вы можете настроить демпинг.", call.message.chat.id, call.message.id, reply_markup=keyboard) bot.answer_callback_query(call.id) def switch_currency(call: telebot.types.CallbackQuery): global SETTINGS currencies = ["₽", "$", "€"] * 2 SETTINGS["currency"] = currencies[currencies.index(SETTINGS["currency"]) + 1] save_settings() open_settings(call) def to_lot_mess(call: telebot.types.CallbackQuery): n = call.data.split(":")[-1] global LOTS if n == "0" and n not in LOTS.keys(): # todo LOTS.setdefault(n, {"on": False, "keywords": "Нужно настроить ключевые слова лота.", "min": 0, "max": 99999, "users_good": False, "users_bad": False, "stars": 5, "ignore_0_stars": False, "step": 5, "divider": 1, "1_currency": False }) save_lots() keyboard = K() keyboard.add(B(f"{'🟢 Включено' if LOTS[n]['on'] else '🔴 Выключено'}", callback_data=f"{CBT_TEXT_EDIT}:{n}:on")) keyboard.add(B(f"✏ Изменить ключевые слова", callback_data=f"{CBT_TEXT_EDIT}:{n}:keywords")) keyboard.add(B(f"✏ ID лота: {n}", callback_data=f"{CBT_TEXT_EDIT}:{n}:lot_id")) keyboard.add( B(f"{'🟢' if LOTS[n]['1_currency'] else '🔴'}" + f" Минимум 1 {SETTINGS['currency']} без учета комиссии", callback_data=f"{CBT_TEXT_EDIT}:{n}:1_currency")) keyboard.add(B(f"✏ Мин. цена: {LOTS[n].get('min')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:min")) keyboard.add(B(f"✏ Макс. цена: {LOTS[n].get('max')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:max")) keyboard.add(B(f"✏ Шаг: {LOTS[n].get('step')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:step")) keyboard.add(B(f"✏ Делитель: {LOTS[n].get('divider')}", callback_data=f"{CBT_TEXT_EDIT}:{n}:divider")) keyboard.add(B(f"{'🟢' if LOTS[n].get('users_good') else '🔴'} Игнор лотов друзей", callback_data=f"{CBT_TEXT_EDIT}:{n}:users_good")) keyboard.add(B(f"{'🟢' if LOTS[n].get('users_bad') else '🔴'} Игнор всех, кроме конкурентов", callback_data=f"{CBT_TEXT_EDIT}:{n}:users_bad")) keyboard.add(B(f"{'🟢' if LOTS[n].get('ignore_0_stars') else '🔴'} Игнор челов без рейтинга", callback_data=f"{CBT_TEXT_EDIT}:{n}:ignore_0_stars")) keyboard.add(B(f"{LOTS[n].get('stars')}+ ⭐", callback_data=f"{CBT_TEXT_EDIT}:{n}:stars")) keyboard.add(B(f"🗑 Удалить", callback_data=f"{CBT_TEXT_DELETE}:{n}")) keyboard.add(B("◀ Назад", callback_data=f"{CBT.PLUGIN_SETTINGS}:{UUID}")) txt = f"https://Funpay.com/lots/offer?id={n}\n\n<b>Ключевые слова:</b> {LOTS.get(n).get('keywords')}" bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.id, text=txt, reply_markup=keyboard) def answer_to_lot_mess(call: telebot.types.CallbackQuery): *a, n, key = call.data.split(":") global LOTS if key in ("max", "min", "keywords", "lot_id", "step", "divider"): bot.answer_callback_query(call.id) d = { "max": "максимальную цену", "min": "минимальную цену", "keywords": "ключевые слова (<code>|</code> для разделения равнозначных значений)", "lot_id": "ID лота", "step": "шаг", "divider": "делитель" } text = f'Введите {d.get(key, "что-то")} для https://Funpay.com/lots/offer?id={n} :' msg = bot.send_message(call.message.chat.id, text=text, reply_markup=tg_bot.static_keyboards.CLEAR_STATE_BTN()) tg.set_state(call.message.chat.id, msg.id, call.from_user.id, CBT_TEXT_EDIT, data={"key": key, "n": n}) bot.answer_callback_query(call.id) return if key in ("on", "users_good", "users_bad", "ignore_0_stars", "1_currency"): LOTS[n][key] = not LOTS[n][key] elif key == "stars": LOTS[n][key] = LOTS[n][key] % 5 + 1 save_lots() call.data = f"{CBT_TEXT_CHANGE_LOT}:{n}" to_lot_mess(call) return def to_delete(call: telebot.types.CallbackQuery): n = call.data.split(":")[-1] global LOTS del LOTS[n] save_lots() open_settings(call) def edited(message: telebot.types.Message): global LOTS text = message.text data = tg.get_state(message.chat.id, message.from_user.id)["data"] if data.get("key") in ("max", "min", "keywords", "step", "divider"): if data.get("key") == "keywords": text = text.lower() else: text = float(text) LOTS[data.get("n")][data.get("key")] = text elif data.get("key") == "lot_id": text = str(int("".join([char for char in text if char.isdigit()]))) LOTS[text] = LOTS[data.get("n")] del LOTS[data.get("n")] tg.clear_state(message.chat.id, message.from_user.id, True) keyboard = K() \ .row(B("◀ Назад", callback_data=f"{CBT_TEXT_CHANGE_LOT}:{data.get('n') if data.get('key') != 'lot_id' else text}")) save_lots() bot.reply_to(message, f"✅", reply_markup=keyboard) def edit_users_list(message: telebot.types.Message): global SETTINGS text = message.text users = text.split(" ")[1:] if (text.startswith("/adl_good")): key = "users_good" elif (text.startswith("/adl_bad")): key = "users_bad" else: raise Exception() for user in users: if user not in SETTINGS[key]: SETTINGS[key].append(user) else: SETTINGS[key].remove(user) save_settings() bot.reply_to(message, f"{'Список друзей' if key == 'users_good' else 'Список конкурентов'}:\n{', '.join([f'<code>{i}</code>' for i in SETTINGS[key]])}") tg.msg_handler(edited, func=lambda m: tg.check_state(m.chat.id, m.from_user.id, CBT_TEXT_EDIT)) tg.msg_handler(edit_users_list, func=lambda m: m.text.startswith("/adl_")) tg.cbq_handler(to_lot_mess, lambda c: c.data.startswith(f"{CBT_TEXT_CHANGE_LOT}:")) tg.cbq_handler(switch_currency, lambda c: f"{CBT_CHANGE_CURRENCY}" in c.data) tg.cbq_handler(open_settings, lambda c: f"{CBT.PLUGIN_SETTINGS}:{UUID}" in c.data) tg.cbq_handler(answer_to_lot_mess, lambda c: c.data.startswith(f"{CBT_TEXT_EDIT}:")) tg.cbq_handler(to_delete, lambda c: c.data.startswith(f"{CBT_TEXT_DELETE}:")) cardinal.add_telegram_commands(UUID, [ (f"adl_good", "Друзья", True), (f"adl_bad", "Конкуренты", True), ]) def post_start(cardinal): def filter_lots(lot: LotShortcut, my_lot_id): global LOTS if lot.seller.username == cardinal.account.username: return False kw = LOTS.get(my_lot_id).get("keywords") if not all([any([j in lot.description.lower() for j in i.split("|")]) for i in kw.split("\n")]): return False if LOTS.get(my_lot_id).get("users_good") and lot.seller.username in SETTINGS.get("users_good"): return False if LOTS.get(my_lot_id).get("users_bad") and lot.seller.username not in SETTINGS.get("users_bad"): return False if not (LOTS.get(my_lot_id).get("min") + LOTS.get(my_lot_id).get("step") <= lot.price <= LOTS.get( my_lot_id).get("max") + LOTS.get(my_lot_id).get("step")): return False if lot.seller.stars is None and LOTS.get(my_lot_id).get("ignore_0_stars"): return False if lot.seller.stars is not None and lot.seller.stars < LOTS.get(my_lot_id).get("stars"): return False return True def get_commission(node_id): headers = { "accept": "*/*", "x-requested-with": "XMLHttpRequest" } data = { "nodeId": node_id, "price": 1000 } calc = cardinal.account.method("post", "https://Funpay.com/lots/calc", headers, data).json()["methods"] commission = float( min([i for i in calc if i.get("unit") == SETTINGS["currency"]], key=lambda x: float(x.get("price"))).get( "price")) / 1000 return commission def change_price(cardinal: Cardinal, my_lot_id: int, new_price: float, commission: float, min_1: bool): lot_fields = cardinal.account.get_lot_fields(my_lot_id) time.sleep(1) price_without_comission = new_price / commission if price_without_comission < 1 and min_1: price_without_comission = 1 old_price = lot_fields.price if r := abs(price_without_comission - old_price) >= 0.001: lot_fields.price = price_without_comission cardinal.account.save_lot(lot_fields) logger.info( f"{LOGGER_PREFIX} Сохранил лот $MAGENTA{my_lot_id}$RESET. Цена: {old_price} -> {price_without_comission}") else: logger.info( f"{LOGGER_PREFIX} Цена лота $MAGENTA{my_lot_id}$RESET не изменилась: {old_price} -> {price_without_comission}") def process(cardinal): global LOTS while True: logger.info(f"{LOGGER_PREFIX} Новый проход. {'-' * 100}") try: to_break = False result = cardinal.update_lots_and_categories() if not result: time.sleep(30) continue my_lots = cardinal.tg_profile.get_sorted_lots(2) for category, i in my_lots.items(): if category.type == SubCategoryTypes.CURRENCY or category.id == 210: continue category_lots = None category_comission = None for my_lot_id, lot in i.items(): if str(lot.currency) != SETTINGS.get("currency"): to_break = True break my_lot_id = str(my_lot_id) if str(my_lot_id) not in LOTS.keys() or not LOTS[my_lot_id].get("on"): continue logger.info(f"{LOGGER_PREFIX} Обрабатываю лот {my_lot_id}.") try: if category_lots is None: category_lots = cardinal.account.get_subcategory_public_lots(category.type, category.id) time.sleep(1) logger.info( f"{LOGGER_PREFIX} Получил {len(category_lots)} лотов категории $MAGENTA{category.fullname}$RESET.") if category_comission is None: category_comission = get_commission(category.id) time.sleep(1) logger.info( f"{LOGGER_PREFIX} Комиссия раздела {category.fullname} - {category_comission}.") category_lots_4_lot = list(filter(lambda x: filter_lots(x, my_lot_id), category_lots)) logger.info( f"{LOGGER_PREFIX} Осталось {len(category_lots_4_lot)} лотов для лота {my_lot_id} после фильрации.") min_1 = LOTS[my_lot_id].get("1_currency", False) if category_lots_4_lot: logger.info( f"{LOGGER_PREFIX} Список подходящих лотов: {', '.join([str(l.id) for l in category_lots_4_lot])}") min_price_lot = min(category_lots_4_lot, key=lambda x: x.price) if str(min_price_lot.currency) != SETTINGS.get("currency"): to_break = True break logger.info(f"{LOGGER_PREFIX} Перебиваем лот {min_price_lot.id} в лоте {my_lot_id}") first = min_price_lot.price - LOTS[my_lot_id].get("step") change_price(cardinal, my_lot_id, min(LOTS[my_lot_id].get("max"), first - first % LOTS[my_lot_id].get("divider")), category_comission, min_1) else: change_price(cardinal, my_lot_id, LOTS[my_lot_id].get("max"), category_comission, min_1) time.sleep(1) except: logger.warning( f"{LOGGER_PREFIX} Произошла ошибка при обработке лота {my_lot_id}. Лот пропущен.") logger.debug("TRACEBACK", exc_info=True) if to_break: break except: logger.warning( f"{LOGGER_PREFIX} Произошла ошибка. Итерация пропущена. Следующая попытка через {SETTINGS.get('time')} минут.") logger.debug("TRACEBACK", exc_info=True) time.sleep(max(SETTINGS.get("time", 0) * 60, 5)) Thread(target=process, daemon=True, args=(cardinal,)).start() BIND_TO_PRE_INIT = [init] BIND_TO_POST_START = [post_start] BIND_TO_DELETE = None сидор, будь мужиком и продавай доверенным лицам свой говнокод