diff --git a/API/ggprice.py b/API/ggprice.py new file mode 100644 index 0000000..6fbe344 --- /dev/null +++ b/API/ggprice.py @@ -0,0 +1,129 @@ +import sqlite3 +import requests +import time + +# ====================== +# CONFIG +# ====================== + +DB_PATH = "rokky.db" + +API_URL = "https://seller.ggsel.com/api_sellers/api/product/edit/prices" +TOKEN = "YOUR_TOKEN" + +BATCH_SIZE = 200 # сколько товаров за раз отправлять +SLEEP_BETWEEN = 1 # задержка между батчами (сек) + +# ====================== +# DB +# ====================== + +def get_products_for_update(conn): + cursor = conn.cursor() + + cursor.execute(""" + SELECT product_id, price + FROM products + WHERE product_id IS NOT NULL + AND price IS NOT NULL + """) + + return cursor.fetchall() + + +# ====================== +# BUILD PAYLOAD +# ====================== + +def build_payload(rows): + payload = [] + + for product_id, price in rows: + payload.append({ + "product_id": int(product_id), + "price": float(price) + }) + + return payload + + +# ====================== +# CHUNKING +# ====================== + +def chunked(data, size): + for i in range(0, len(data), size): + yield data[i:i + size] + + +# ====================== +# SEND REQUEST +# ====================== + +def send_prices(payload): + try: + response = requests.post( + f"{API_URL}?token={TOKEN}", + json=payload, + timeout=30 + ) + + print("STATUS:", response.status_code) + + if response.status_code != 200: + print("ERROR:", response.text) + return None + + data = response.json() + print("RESPONSE:", data) + + return data + + except Exception as e: + print("REQUEST ERROR:", e) + return None + + +# ====================== +# SYNC +# ====================== + +def sync_prices(): + conn = sqlite3.connect(DB_PATH) + + rows = get_products_for_update(conn) + payload = build_payload(rows) + + total = len(payload) + print(f"Всего товаров: {total}") + + if total == 0: + print("Нет данных для обновления") + return + + sent = 0 + + for batch in chunked(payload, BATCH_SIZE): + print(f"\nОтправка: {sent} → {sent + len(batch)}") + + result = send_prices(batch) + + if result is None: + print("Ошибка при отправке, стоп") + break + + sent += len(batch) + + time.sleep(SLEEP_BETWEEN) + + print(f"\nГотово. Отправлено: {sent}/{total}") + + conn.close() + + +# ====================== +# RUN +# ====================== + +if __name__ == "__main__": + sync_prices() \ No newline at end of file diff --git a/API/ww b/API/ww index e69de29..9ec6946 100644 --- a/API/ww +++ b/API/ww @@ -0,0 +1 @@ +5110eb4a7aa7c60d66f9648db40027f241c52673a6a7c9a136e2a02fd1711fb&amount=1.0¤cy=RUB&date=2026-03-20T10=43=09+03=00&email=rusinowdima2@yandex.ru&id_d=102180432&id_i=21327450&ip=188.32.208.151&is_my_product=true \ No newline at end of file diff --git a/app/__pycache__/routes.cpython-313.pyc b/app/__pycache__/routes.cpython-313.pyc index b807e07..7933f18 100644 Binary files a/app/__pycache__/routes.cpython-313.pyc and b/app/__pycache__/routes.cpython-313.pyc differ diff --git a/app/routes.py b/app/routes.py index 828bfa2..f0651e9 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,4 +1,6 @@ -from flask import Blueprint, request, jsonify +# from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, send_from_directory +import os import sqlite3 import json # from services.rokky import ones @@ -11,11 +13,16 @@ import logging main = Blueprint("main", __name__) DB_PATH = "./files/rokky.db" +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +IMAGE_FOLDER = os.path.join(BASE_DIR, 'images') + @main.route("/") def index(): return "Hello wstkeys!!!" - +@main.route("/images/") +def get_image(filename): + return send_from_directory(IMAGE_FOLDER, filename) @main.route("/orders/api_payments", methods=["POST", "GET"]) def index1(): diff --git a/images/1.jpg b/images/1.jpg new file mode 100644 index 0000000..8eb248c Binary files /dev/null and b/images/1.jpg differ diff --git a/main.py b/main.py index 1974654..fb6400a 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,9 @@ logging.basicConfig( app = create_app() + + + start_scheduler() if __name__ == "__main__": diff --git a/services/__pycache__/tim.cpython-313.pyc b/services/__pycache__/tim.cpython-313.pyc index 77c4830..ae28a50 100644 Binary files a/services/__pycache__/tim.cpython-313.pyc and b/services/__pycache__/tim.cpython-313.pyc differ diff --git a/services/gg.py b/services/gg.py index f5bbca2..bce400a 100644 --- a/services/gg.py +++ b/services/gg.py @@ -170,7 +170,7 @@ def get_messages(id_i, id_from=None, id_to=None, newer=None, count=50): # пример -print(get_messages(id_i=18840912)) +# print(get_messages(id_i=18840912)) def get_chats(page=1, pagesize=20, filter_new=None, email=None, id_ds=None): @@ -212,6 +212,28 @@ def get_chats(page=1, pagesize=20, filter_new=None, email=None, id_ds=None): return r.json() +def get_product(): + token = load_token() + product_id = 102182541 + + url = f"https://seller.ggsel.com/api_sellers/api/products/{product_id}/data" + + params = { + "token": token + } + + headers = { + "Accept": "application/json" + } + + r = requests.get(url, headers=headers, params=params) + + print(r.status_code) + return r.json() + + +print(get_product()) + # пример # print(get_chats()) # def tokens(): diff --git a/services/mailer.py b/services/mailer.py new file mode 100644 index 0000000..68d0e7d --- /dev/null +++ b/services/mailer.py @@ -0,0 +1,55 @@ +import os +import smtplib +from email.message import EmailMessage +from dotenv import load_dotenv + +from utils import build_items_html, build_keys_html, render_template + +load_dotenv() + +EMAIL_ADDRESS = os.getenv("EMAIL_ADDRESS") +EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD") + + +def send_plain_email(to_address: str, subject: str, body: str): + msg = EmailMessage() + msg['Subject'] = subject + msg['From'] = EMAIL_ADDRESS + msg['To'] = to_address + msg.set_content(body) + + with smtplib.SMTP('smtp.gmail.com', 587) as smtp: + smtp.starttls() + smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD) + smtp.send_message(msg) + + print(f"Plain email sent to {to_address}") + + +def send_html_email(to_address: str, data: dict): + msg = EmailMessage() + msg['Subject'] = f"Order #{data['order_id']}" + msg['From'] = EMAIL_ADDRESS + msg['To'] = to_address + + with open('templates/welcome.html', 'r', encoding='utf-8') as f: + html_template = f.read() + + html_template = html_template.replace( + "{{items}}", build_items_html(data["items"]) + ) + + html_template = html_template.replace( + "{{keys_block}}", build_keys_html(data.get("keys")) + ) + + html_content = render_template(html_template, data) + + msg.add_alternative(html_content, subtype='html') + + with smtplib.SMTP('smtp.gmail.com', 587) as smtp: + smtp.starttls() + smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD) + smtp.send_message(msg) + + print(f"HTML email sent to {to_address}") \ No newline at end of file diff --git a/services/send_mai.py b/services/send_mai.py new file mode 100644 index 0000000..b77289d --- /dev/null +++ b/services/send_mai.py @@ -0,0 +1,66 @@ +import sys +from mailer import send_plain_email, send_html_email + + +def read_multiline(prompt: str) -> str: + print(prompt) + print("(finish with ';')") + + lines = [] + while True: + line = input() + if line == ';': + break + lines.append(line) + + return '\n'.join(lines) + + +def send_text_flow(): + to_addr = input("Receiver email: ").strip() + subject = input("Subject: ").strip() or "(No subject)" + body = read_multiline("Message:") + + send_plain_email(to_addr, subject, body) + + +def send_html_flow(): + to_addr = input("Receiver email: ").strip() + + data = { + "company_name": "wstkeys", + "order_id": "123", + "customer_name": "4321", + "currency": "USD", + "items": [ + {"name": "Game 1333", "quantity": 1, "price": 1330}, + + ], + "date": "23.03.2026 14:02", + "product_name": "Railroads Online - Pioneer DLC", + "product_image": "https://s3.ggsel.com/gsellers-imgs-prod/e05c8d1f28e18f56334bf8e7e9f7b547.jpeg", + "total_price": 20, + "key": 'XXXX-XXXX-YYY23', + "support_email": "wstkeys@gmail.com", + "year": 2026 + } + + send_html_email(to_addr, data) + + +def main(): + print("1 - Send plain email") + print("2 - Send HTML email") + + choice = input("Choose: ").strip() + + if choice == "1": + send_text_flow() + elif choice == "2": + send_html_flow() + else: + print("Invalid choice") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/services/template_engine.py b/services/template_engine.py new file mode 100644 index 0000000..b77754a --- /dev/null +++ b/services/template_engine.py @@ -0,0 +1,28 @@ +def build_items_html(items): + rows = "" + for item in items: + rows += f""" + + {item['name']} + {item['quantity']} + {item['price']} + + """ + return rows + + +def build_keys_html(keys): + if not keys: + return "" + + rows = "

Your Keys:

" + return rows + + +def render_template(template_str, data): + for key, value in data.items(): + template_str = template_str.replace(f"{{{{{key}}}}}", str(value)) + return template_str \ No newline at end of file diff --git a/services/tim.py b/services/tim.py index 17afbc0..d767e7c 100644 --- a/services/tim.py +++ b/services/tim.py @@ -8,5 +8,7 @@ def start_scheduler(): # каждые 10 секунд scheduler.add_job(run_task, 'interval', minutes=10) + # Раз в сутки (24 часа) + scheduler.add_job(run_task, 'interval', days=1) scheduler.start() \ No newline at end of file diff --git a/services/utils.py b/services/utils.py new file mode 100644 index 0000000..13f4354 --- /dev/null +++ b/services/utils.py @@ -0,0 +1,28 @@ +def build_items_html(items): + rows = "" + for item in items: + rows += f""" + + {item['name']} + {item['quantity']} + {item['price']} + + """ + return rows + + +def build_keys_html(keys): + if not keys: + return "" + + rows = "

Keys:

" + return rows + + +def render_template(template_str, data): + for key, value in data.items(): + template_str = template_str.replace(f"{{{{{key}}}}}", str(value)) + return template_str \ No newline at end of file diff --git a/templates/welcome.html b/templates/welcome.html new file mode 100644 index 0000000..c03fce4 --- /dev/null +++ b/templates/welcome.html @@ -0,0 +1,54 @@ + + + + + + + + +
+ + +
+ logo +
+ +

Спасибо за покупку в WST Keys (West Store Trusted Keys)

+ +
+ + +

Дата покупки: {{date}}

+

Номер заказа: {{order_id}}

+ +

{{product_name}}

+ + + + +

Сумма: {{total_price}} {{currency}}

+ +
+ + +

Цифровой ключ

+
+ {{key}} +
+ +
+ + +

+ +

+ +

+Если у вас возникнут вопросы, напишите в личные сообщения или свяжитесь с нами по электронной почте
{{support_email}} +

+ +
+ + + \ No newline at end of file