diff --git a/Dockerfile b/Dockerfile index cdb571c..f15281c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,4 @@ RUN chown service:service -R /home/service && chmod 775 -R /home/service USER service -ENTRYPOINT ["python", "tradebot.facti13.py"] \ No newline at end of file +ENTRYPOINT ["python"] \ No newline at end of file diff --git a/backend_integration.py b/backend_integration.py new file mode 100644 index 0000000..f6ba360 --- /dev/null +++ b/backend_integration.py @@ -0,0 +1,52 @@ +import aiohttp, os, sys +from colors import * +import traceback +from steam import SteamID + +class BackendClient: + up = False + + def __init__(self): + if not os.getenv("BACKEND_URL", ""): + error("BACKEND_URL not setted in env") + sys.exit(10) + if not os.getenv("SECRET_KEY", ""): + error("SECRET_KEY not setted in env") + sys.exit(11) + self.secret_key = os.getenv("SECRET_KEY") + self.pulse_url = f"{os.getenv('BACKEND_URL')}/api/pulse" + self.vip_url = f"{os.getenv('BACKEND_URL')}/api/external/vip" + + async def pulse(self): + async with aiohttp.ClientSession(cookies={ + "secretkey":self.secret_key}) as session: + async with session.get(self.pulse, ssl=False) as response: + try: + await response.text() + self.up = True + except: + error("Backend not respond") + traceback.print_exc() + self.up = False + return self.up + + async def vip(self, steamid: SteamID, amount: int, extra: str): + async with aiohttp.ClientSession(cookies={ + "secretkey":self.secret_key}) as session: + async with session.post(self.vip_url + f"?steam={steamid.id64}&amount={amount}&service=steam&extra={extra}", ssl=False) as response: + try: + result = int(await response.text()) + if result == 0: + warning(f"[S64:{steamid.id64}] VIP as not be added, maybe permition already exists") + return 99 + elif result > 0: + info(f"[S64:{steamid.id64}] VIP has be added!") + return 100 + elif result < 0: + info(f"[S64:{steamid.id64}] VIP has be extends!") + return 101 + except: + error(f"[S64:{steamid.id64}] Backend returned error") + traceback.print_exc() + return False + return False diff --git a/docker-compose.yaml b/docker-compose.yaml index 7038ecf..2a13d7d 100644 Binary files a/docker-compose.yaml and b/docker-compose.yaml differ diff --git a/git_build/Dockerfile b/git_build/Dockerfile new file mode 100644 index 0000000..c825f1b --- /dev/null +++ b/git_build/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.10 +RUN python -m pip install aiomysql aiohttp steamio git+https://github.com/conqp/rcon +WORKDIR /app + +ENV TZ=Europe/Moscow +ENV PYTHONUNBUFFERED 1 +RUN mkdir /home/service && \ +cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \ +dpkg-reconfigure -f noninteractive tzdata + +ENV BUILDDATE ${BUILDDATE} +RUN cd /tmp && git clone https://git.pblr-nyk.pro/gsd/Facti13.SteamTradeBot.v2 && cp -a Facti13.SteamTradeBot.v2/. /app && rm -r Facti13.SteamTradeBot.v2 +ENTRYPOINT ["python"] \ No newline at end of file diff --git a/tradebot.facti13.external.py b/tradebot.facti13.external.py new file mode 100644 index 0000000..e65da21 --- /dev/null +++ b/tradebot.facti13.external.py @@ -0,0 +1,185 @@ +import steam as SteamPy +import argparse, os, sys +from colors import * +from json import load +import asyncio + +from backend_integration import BackendClient + +class TradeChecker: + APP_ID = 440 + MONTH = 2678400 + WEEK = 604800 + DAY = 86400 + + def mannco_key(self, items): + class_id = 101785959 + instance_id = 11040578 + count = 0 + for item in items: + #print(f"{item._app_id} {item.class_id} {item.instance_id}") + if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: + count += 1 + return count + + def pure_metal(self, items): + class_id = 2674 + instance_id = 11040547 + count = 0 + for item in items: + #print(f"{item._app_id} {item.class_id} {item.instance_id}") + if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: + count += 1 + return count + + def Items2Seconds(self, items): + key_count = self.mannco_key(items) + metal_count = self.pure_metal(items) + final_amount = 0 + #print(key_count, ",", metal_count) + final_amount += key_count * self.MONTH + if(metal_count >= 20): + final_amount += (metal_count / 20) * self.WEEK + elif(metal_count >= 5): + final_amount += (metal_count / 5) * self.DAY + + return final_amount + + +async def send_msg(trade: SteamPy.TradeOffer, message: str): + try: + await trade.partner.send(message) + except: + error(f"[{trade.id}] Cannot send message") + +class SteamClient(SteamPy.Client): + items = TradeChecker() + trade_tracker = {} + backend = BackendClient() + + async def on_ready(self): + info(f"Logged in as: {self.user}") + + async def on_connect(self): + await self.backend.pulse() + ok("Success connect to steam") + + async def on_disconnect(self): + await self.backend.pulse() + ok("Disconnect from steam") + + #Проверка шмота на леквид + async def on_trade_receive(self, trade: SteamPy.TradeOffer): + info(f"[{trade.id}] Incoming trade from [{trade.partner.id}] {trade.partner.name}") + + if trade.state != SteamPy.enums.TradeOfferState.Active: + error(f"[{trade.id}] trade have not active stats") + await trade.decline() + return + + if not trade.is_gift(): + warning(f"[{trade.id}] partner wanna get bot items, decine trade") + await trade.decline() + return + + if len(trade.items_to_receive) > 25: + warning(f"[{trade.id}] cannot accept trade with more 25 items") + await trade.decline() + return + + seconds = self.items.Items2Seconds(trade.items_to_receive) + if seconds == 0: + error(f"[{trade.id}] cannot accept trade not valid items") + await trade.decline() + return + + pulse_result = await self.backend.pulse() + if not pulse_result: + error("Backend not response ot pulse, drop trade") + return + + info(f"[{trade.id}] Try accept trade") + await trade.accept() + info(f"[{trade.id}] Maybe trade acceped...") + #Force on trade accept + await asyncio.sleep(3) + if not trade.id in self.trade_tracker: + self.trade_tracker[trade.id] = 60 + + while self.trade_tracker[trade.id] > 2: + await asyncio.sleep(1) + self.trade_tracker[trade.id] -= 1 + await self.on_trade_accept(trade) + return + + #Принятый трейд + async def on_trade_accept(self, trade: SteamPy.TradeOffer): + info(f"[{trade.id}] Finalize accept") + if trade.id in self.trade_tracker and self.trade_tracker[trade.id] == 0: + return + + if trade.id in self.trade_tracker and self.trade_tracker[trade.id] > 0: + self.trade_tracker[trade.id] = 0 + + if not trade.id in self.trade_tracker: + self.trade_tracker[trade.id] = 0 + + if not trade.is_gift(): + return + + info(f"[{trade.id}] Final stage accepted trade [{trade.partner.id}] {trade.partner.name}") + pulse_result = await self.backend.pulse() + if not pulse_result: + warning("Backend not responde, any time try add vip") + extra = f"keys={self.items.mannco_key(trade.items_to_receive)};metal={self.items.pure_metal(trade.items_to_receive)};" + seconds = self.items.Items2Seconds(trade.items_to_receive) + result = await self.backend.vip(trade.partner, seconds, extra) + + if result == 99: + await send_msg(trade, "Спасибо за вещи, но у тебя уже есть КРУТЫЕ права на фактах") + elif result == 100: + await send_msg(trade, f"Мои поздавления! Ты получил випку на {seconds / 60 / 60 / 24} дней") + elif result == 101: + await send_msg(trade, f"Круто! Ты продлил випку на {seconds / 60 / 60 / 24} дней") + else: + await send_msg(trade, "Произошла ошибка добавление випа, сообщи об этом разрабу") + + return + +if __name__ == "__main__": + print(f"Build date: {os.getenv('BUILDDATE', 'not set')}") + parser = argparse.ArgumentParser() + parser.add_argument("--auth-file", type=str, default=os.environ.get("AUTH_FILE", "")) + parser.add_argument("--steam-secret", type=str, default=os.environ.get("STEAM_SECRET", "")) + args = parser.parse_args() + + client = SteamClient(game = SteamPy.Game(id=440)) + + if args.auth_file and args.steam_secret: + try: + with open(args.auth_file, "r") as auth_file: + login = auth_file.readline()[:-1] + password = auth_file.readline() + except: + error("steam auth data is not correct") + sys.exit(3) + + try: + with open(args.steam_secret, "r") as steam_file: + secrets = load(steam_file) + #shared_secret + #identity_secret + except: + error("steam secret is not correct") + sys.exit(4) + else: + error("steam auth data or steam secret not be setted") + sys.exit(2) + + + client.run( + username=login, + password = password, + shared_secret=secrets["shared_secret"], + identity_secret=secrets["identity_secret"], + ) diff --git a/tradebot.facti13.py b/tradebot.facti13.internal.py similarity index 96% rename from tradebot.facti13.py rename to tradebot.facti13.internal.py index 7acef35..e1b442e 100644 --- a/tradebot.facti13.py +++ b/tradebot.facti13.internal.py @@ -1,211 +1,210 @@ -from cmath import inf -from pydoc import cli -import steam as SteamPy -import argparse, os, sys -from colors import * -from db_driver import Database -from json import load -from discord_alarm import DiscordAlarm -from post_effect import PostEffect -import asyncio - -class TradeChecker: - APP_ID = 440 - MONTH = 2678400 - WEEK = 604800 - DAY = 86400 - - def mannco_key(self, items): - class_id = 101785959 - instance_id = 11040578 - count = 0 - for item in items: - #print(f"{item._app_id} {item.class_id} {item.instance_id}") - if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: - count += 1 - return count - - def pure_metal(self, items): - class_id = 2674 - instance_id = 11040547 - count = 0 - for item in items: - #print(f"{item._app_id} {item.class_id} {item.instance_id}") - if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: - count += 1 - return count - - def Items2Seconds(self, items): - key_count = self.mannco_key(items) - metal_count = self.pure_metal(items) - final_amount = 0 - #print(key_count, ",", metal_count) - final_amount += key_count * self.MONTH - if(metal_count >= 20): - final_amount += (metal_count / 20) * self.WEEK - elif(metal_count >= 5): - final_amount += (metal_count / 5) * self.DAY - - return final_amount - - -async def send_msg(trade: SteamPy.TradeOffer, message: str): - try: - await trade.partner.send(message) - except: - error(f"[{trade.id}] Cannot send message") - -class SteamClient(SteamPy.Client): - db = None - items = TradeChecker() - discord_alarm = None - post_effect = None - - trade_tracker = {} - - async def on_ready(self): - info(f"Logged in as: {self.user}") - - async def on_connect(self): - ok("Success connect to steam") - await self.db.connect() - - async def on_disconnect(self): - ok("Disconnect from steam") - await self.db.disconnect() - - #Проверка шмота на леквид - async def on_trade_receive(self, trade: SteamPy.TradeOffer): - info(f"[{trade.id}] Incoming trade from [{trade.partner.id}] {trade.partner.name}") - - if trade.state != SteamPy.enums.TradeOfferState.Active: - error(f"[{trade.id}] trade have not active stats") - #await send_msg(trade, "Бот не сможет принять трейд от тебя по причине удержания вещей с твоей стороны") - await trade.decline() - return - - if not trade.is_gift(): - warning(f"[{trade.id}] partner wanna get bot items, decine trade") - #await send_msg(trade, "Не пытайся спиздить вещи у бота") - await trade.decline() - return - - if len(trade.items_to_receive) > 25: - warning(f"[{trade.id}] cannot accept trade with more 25 items") - #await send_msg(trade, "За один раз можно отправить неболее 25 вещей") - await trade.decline() - return - - seconds = self.items.Items2Seconds(trade.items_to_receive) - if seconds == 0: - error(f"[{trade.id}] cannot accept trade not valid items") - #await send_msg(trade, "Ты отправил неправльные вещи! Проверь что это:\n- Ключ МаннКо (уникальный)\n- Очищенный метал (уникальный)") - await trade.decline() - return - - info(f"[{trade.id}] Try accept trade") - #await send_msg(trade, "Попытка принять трейд...") - await trade.accept() - info(f"[{trade.id}] Maybe trade acceped...") - #Force on trade accept - await asyncio.sleep(3) - if not trade.id in self.trade_tracker: - self.trade_tracker[trade.id] = 60 - - while self.trade_tracker[trade.id] > 2: - await asyncio.sleep(1) - self.trade_tracker[trade.id] -= 1 - await self.on_trade_accept(trade) - return - - #Принятый трейд - async def on_trade_accept(self, trade: SteamPy.TradeOffer): - info(f"[{trade.id}] Finalize accept") - if trade.id in self.trade_tracker and self.trade_tracker[trade.id] == 0: - return - - if trade.id in self.trade_tracker and self.trade_tracker[trade.id] > 0: - self.trade_tracker[trade.id] = 0 - - if not trade.id in self.trade_tracker: - self.trade_tracker[trade.id] = 0 - - if not trade.is_gift(): - return - - info(f"[{trade.id}] Final stage accepted trade [{trade.partner.id}] {trade.partner.name}") - #await send_msg(trade, "Успешно, сейчас посчитаем сколько ты надонатил") - - seconds = self.items.Items2Seconds(trade.items_to_receive) - result = await self.db.processing_vip(trade.partner, seconds) - await self.discord_alarm.alert(trade.partner, seconds) - await self.post_effect.execute() - - if result == False: - await send_msg(trade, "Спасибо за вещи, но у тебя уже есть КРУТЫЕ права на фактах") - elif result == 100: - await send_msg(trade, f"Мои поздавления! Ты получил випку на {seconds / 60 / 60 / 24} дней") - elif result == 101: - await send_msg(trade, f"Круто! Ты продлил випку на {seconds / 60 / 60 / 24} дней") - - return - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--auth-file", type=str, default=os.environ.get("AUTH_FILE", "")) - parser.add_argument("--steam-secret", type=str, default=os.environ.get("STEAM_SECRET", "")) - parser.add_argument("--db-host", type=str, default=os.environ.get("DB_HOST", "")) - parser.add_argument("--db-username", type=str, default=os.environ.get("DB_USERNAME", "")) - parser.add_argument("--db-password", type=str, default=os.environ.get("DB_PASSWORD", "")) - parser.add_argument("--db-name", type=str, default=os.environ.get("DB_NAME", "")) - parser.add_argument("--discord-url", type=str, default=os.environ.get("DISCORD_WEBHOOK_URL", "")) - parser.add_argument("--servers-list", type=str, default=os.environ.get("SERVERS_LIST", "")) - args = parser.parse_args() - - client = SteamClient(game = SteamPy.Game(id=440)) - - if args.db_host and args.db_username and args.db_password and args.db_name: - client.db = Database(args.db_host, args.db_username, args.db_password, args.db_name) - else: - error("DB data not be setted") - sys.exit(1) - - if args.auth_file and args.steam_secret: - try: - with open(args.auth_file, "r") as auth_file: - login = auth_file.readline()[:-1] - password = auth_file.readline() - except: - error("steam auth data is not correct") - sys.exit(3) - - try: - with open(args.steam_secret, "r") as steam_file: - secrets = load(steam_file) - #shared_secret - #identity_secret - except: - error("steam secret is not correct") - sys.exit(4) - else: - error("steam auth data or steam secret not be setted") - sys.exit(2) - - if args.discord_url: - client.discord_alarm = DiscordAlarm(args.discord_url) - else: - error("need discord webhook") - sys.exit(5) - - if args.servers_list: - client.post_effect = PostEffect(args.servers_list) - else: - error("need servers list file") - sys.exit(6) - - client.run( - username=login, - password = password, - shared_secret=secrets["shared_secret"], - identity_secret=secrets["identity_secret"], - ) +import steam as SteamPy +import argparse, os, sys +from colors import * +from json import load +import asyncio + +from db_driver import Database +from discord_alarm import DiscordAlarm +from post_effect import PostEffect + +class TradeChecker: + APP_ID = 440 + MONTH = 2678400 + WEEK = 604800 + DAY = 86400 + + def mannco_key(self, items): + class_id = 101785959 + instance_id = 11040578 + count = 0 + for item in items: + #print(f"{item._app_id} {item.class_id} {item.instance_id}") + if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: + count += 1 + return count + + def pure_metal(self, items): + class_id = 2674 + instance_id = 11040547 + count = 0 + for item in items: + #print(f"{item._app_id} {item.class_id} {item.instance_id}") + if int(item._app_id) == self.APP_ID and int(item.class_id) == class_id and int(item.instance_id) == instance_id: + count += 1 + return count + + def Items2Seconds(self, items): + key_count = self.mannco_key(items) + metal_count = self.pure_metal(items) + final_amount = 0 + #print(key_count, ",", metal_count) + final_amount += key_count * self.MONTH + if(metal_count >= 20): + final_amount += (metal_count / 20) * self.WEEK + elif(metal_count >= 5): + final_amount += (metal_count / 5) * self.DAY + + return final_amount + + +async def send_msg(trade: SteamPy.TradeOffer, message: str): + try: + await trade.partner.send(message) + except: + error(f"[{trade.id}] Cannot send message") + +class SteamClient(SteamPy.Client): + db = None + items = TradeChecker() + discord_alarm = None + post_effect = None + + trade_tracker = {} + + async def on_ready(self): + info(f"Logged in as: {self.user}") + + async def on_connect(self): + ok("Success connect to steam") + await self.db.connect() + + async def on_disconnect(self): + ok("Disconnect from steam") + await self.db.disconnect() + + #Проверка шмота на леквид + async def on_trade_receive(self, trade: SteamPy.TradeOffer): + info(f"[{trade.id}] Incoming trade from [{trade.partner.id}] {trade.partner.name}") + + if trade.state != SteamPy.enums.TradeOfferState.Active: + error(f"[{trade.id}] trade have not active stats") + #await send_msg(trade, "Бот не сможет принять трейд от тебя по причине удержания вещей с твоей стороны") + await trade.decline() + return + + if not trade.is_gift(): + warning(f"[{trade.id}] partner wanna get bot items, decine trade") + #await send_msg(trade, "Не пытайся спиздить вещи у бота") + await trade.decline() + return + + if len(trade.items_to_receive) > 25: + warning(f"[{trade.id}] cannot accept trade with more 25 items") + #await send_msg(trade, "За один раз можно отправить неболее 25 вещей") + await trade.decline() + return + + seconds = self.items.Items2Seconds(trade.items_to_receive) + if seconds == 0: + error(f"[{trade.id}] cannot accept trade not valid items") + #await send_msg(trade, "Ты отправил неправльные вещи! Проверь что это:\n- Ключ МаннКо (уникальный)\n- Очищенный метал (уникальный)") + await trade.decline() + return + + info(f"[{trade.id}] Try accept trade") + #await send_msg(trade, "Попытка принять трейд...") + await trade.accept() + info(f"[{trade.id}] Maybe trade acceped...") + #Force on trade accept + await asyncio.sleep(3) + if not trade.id in self.trade_tracker: + self.trade_tracker[trade.id] = 60 + + while self.trade_tracker[trade.id] > 2: + await asyncio.sleep(1) + self.trade_tracker[trade.id] -= 1 + await self.on_trade_accept(trade) + return + + #Принятый трейд + async def on_trade_accept(self, trade: SteamPy.TradeOffer): + info(f"[{trade.id}] Finalize accept") + if trade.id in self.trade_tracker and self.trade_tracker[trade.id] == 0: + return + + if trade.id in self.trade_tracker and self.trade_tracker[trade.id] > 0: + self.trade_tracker[trade.id] = 0 + + if not trade.id in self.trade_tracker: + self.trade_tracker[trade.id] = 0 + + if not trade.is_gift(): + return + + info(f"[{trade.id}] Final stage accepted trade [{trade.partner.id}] {trade.partner.name}") + #await send_msg(trade, "Успешно, сейчас посчитаем сколько ты надонатил") + + seconds = self.items.Items2Seconds(trade.items_to_receive) + result = await self.db.processing_vip(trade.partner, seconds) + await self.discord_alarm.alert(trade.partner, seconds) + await self.post_effect.execute() + + if result == False: + await send_msg(trade, "Спасибо за вещи, но у тебя уже есть КРУТЫЕ права на фактах") + elif result == 100: + await send_msg(trade, f"Мои поздавления! Ты получил випку на {seconds / 60 / 60 / 24} дней") + elif result == 101: + await send_msg(trade, f"Круто! Ты продлил випку на {seconds / 60 / 60 / 24} дней") + + return + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--auth-file", type=str, default=os.environ.get("AUTH_FILE", "")) + parser.add_argument("--steam-secret", type=str, default=os.environ.get("STEAM_SECRET", "")) + parser.add_argument("--db-host", type=str, default=os.environ.get("DB_HOST", "")) + parser.add_argument("--db-username", type=str, default=os.environ.get("DB_USERNAME", "")) + parser.add_argument("--db-password", type=str, default=os.environ.get("DB_PASSWORD", "")) + parser.add_argument("--db-name", type=str, default=os.environ.get("DB_NAME", "")) + parser.add_argument("--discord-url", type=str, default=os.environ.get("DISCORD_WEBHOOK_URL", "")) + parser.add_argument("--servers-list", type=str, default=os.environ.get("SERVERS_LIST", "")) + args = parser.parse_args() + + client = SteamClient(game = SteamPy.Game(id=440)) + + if args.db_host and args.db_username and args.db_password and args.db_name: + client.db = Database(args.db_host, args.db_username, args.db_password, args.db_name) + else: + error("DB data not be setted") + sys.exit(1) + + if args.auth_file and args.steam_secret: + try: + with open(args.auth_file, "r") as auth_file: + login = auth_file.readline()[:-1] + password = auth_file.readline() + except: + error("steam auth data is not correct") + sys.exit(3) + + try: + with open(args.steam_secret, "r") as steam_file: + secrets = load(steam_file) + #shared_secret + #identity_secret + except: + error("steam secret is not correct") + sys.exit(4) + else: + error("steam auth data or steam secret not be setted") + sys.exit(2) + + if args.discord_url: + client.discord_alarm = DiscordAlarm(args.discord_url) + else: + error("need discord webhook") + sys.exit(5) + + if args.servers_list: + client.post_effect = PostEffect(args.servers_list) + else: + error("need servers list file") + sys.exit(6) + + client.run( + username=login, + password = password, + shared_secret=secrets["shared_secret"], + identity_secret=secrets["identity_secret"], + )