xync-bot 0.3.7.dev1__tar.gz → 0.3.24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xync-bot might be problematic. Click here for more details.
- {xync_bot-0.3.7.dev1/xync_bot.egg-info → xync_bot-0.3.24}/PKG-INFO +3 -1
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/pyproject.toml +2 -0
- xync_bot-0.3.24/xync_bot/__main__.py +36 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/loader.py +2 -0
- xync_bot-0.3.24/xync_bot/routers/__init__.py +41 -0
- xync_bot-0.3.24/xync_bot/routers/cond/__init__.py +91 -0
- xync_bot-0.3.24/xync_bot/routers/cond/func.py +118 -0
- xync_bot-0.3.7.dev1/xync_bot/routers/main.py → xync_bot-0.3.24/xync_bot/routers/main/handler.py +53 -86
- xync_bot-0.3.24/xync_bot/routers/pay/cd.py +49 -0
- xync_bot-0.3.24/xync_bot/routers/pay/dep.py +270 -0
- xync_bot-0.3.24/xync_bot/routers/pay/handler.py +247 -0
- xync_bot-0.3.24/xync_bot/routers/pay/window.py +244 -0
- xync_bot-0.3.24/xync_bot/shared.py +24 -0
- xync_bot-0.3.24/xync_bot/store.py +150 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24/xync_bot.egg-info}/PKG-INFO +3 -1
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot.egg-info/SOURCES.txt +9 -3
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot.egg-info/requires.txt +2 -0
- xync_bot-0.3.7.dev1/xync_bot/__init__.py +0 -20
- xync_bot-0.3.7.dev1/xync_bot/routers/__init__.py +0 -8
- xync_bot-0.3.7.dev1/xync_bot/routers/cond/__init__.py +0 -179
- xync_bot-0.3.7.dev1/xync_bot/shared.py +0 -5
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/.env.dist +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/.gitignore +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/.pre-commit-config.yaml +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/makefile +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/setup.cfg +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/test_main.http +0 -0
- /xync_bot-0.3.7.dev1/xync_bot/routers/cond/cond.py → /xync_bot-0.3.24/xync_bot/routers/main/__init__.py +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/routers/order.py +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/routers/photo.py +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/routers/vpn.py +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/routers/xicon.png +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot/typs.py +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot.egg-info/dependency_links.txt +0 -0
- {xync_bot-0.3.7.dev1 → xync_bot-0.3.24}/xync_bot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xync-bot
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.24
|
|
4
4
|
Summary: Telegram bot with web app for xync net
|
|
5
5
|
Author-email: Artemiev <mixartemev@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -8,6 +8,8 @@ Project-URL: Homepage, https://gitlab.com/xync/back/tg-bot
|
|
|
8
8
|
Project-URL: Repository, https://gitlab.com/xync/back/tg-bot
|
|
9
9
|
Keywords: aiogram,cyrtranslit,xync-net
|
|
10
10
|
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: cyrtranslit
|
|
12
|
+
Requires-Dist: xn-auth
|
|
11
13
|
Requires-Dist: xync-schema
|
|
12
14
|
Provides-Extra: dev
|
|
13
15
|
Requires-Dist: PGram; extra == "dev"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from asyncio import run
|
|
3
|
+
|
|
4
|
+
from PGram import Bot
|
|
5
|
+
from aiogram.client.default import DefaultBotProperties
|
|
6
|
+
from aiogram.enums import UpdateType
|
|
7
|
+
from x_model import init_db
|
|
8
|
+
|
|
9
|
+
from xync_bot.routers.main.handler import mr
|
|
10
|
+
from xync_bot.routers.cond import cr
|
|
11
|
+
from xync_bot.routers.pay.handler import pr
|
|
12
|
+
from xync_bot.routers import last
|
|
13
|
+
from xync_bot.routers.pay.dep import Store
|
|
14
|
+
|
|
15
|
+
au = [
|
|
16
|
+
UpdateType.MESSAGE,
|
|
17
|
+
UpdateType.CALLBACK_QUERY,
|
|
18
|
+
UpdateType.CHAT_MEMBER,
|
|
19
|
+
UpdateType.MY_CHAT_MEMBER,
|
|
20
|
+
] # , UpdateType.CHAT_JOIN_REQUEST
|
|
21
|
+
bot = Bot([cr, pr, mr, last], Store(), au, default=DefaultBotProperties(parse_mode="HTML"))
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
from xync_bot.loader import TOKEN, TORM
|
|
25
|
+
|
|
26
|
+
logging.basicConfig(level=logging.INFO)
|
|
27
|
+
|
|
28
|
+
async def main() -> None:
|
|
29
|
+
cn = await init_db(TORM)
|
|
30
|
+
bot.dp.workflow_data["store"].glob = await Store.Global() # todo: refact store loading
|
|
31
|
+
await bot.start(
|
|
32
|
+
TOKEN,
|
|
33
|
+
cn,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
run(main())
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from aiogram import Router, F
|
|
4
|
+
from aiogram.enums import ContentType
|
|
5
|
+
from aiogram.exceptions import TelegramBadRequest
|
|
6
|
+
from aiogram.types import Message
|
|
7
|
+
|
|
8
|
+
last = Router(name="last")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@last.message(
|
|
12
|
+
F.content_type.not_in(
|
|
13
|
+
{
|
|
14
|
+
ContentType.NEW_CHAT_MEMBERS,
|
|
15
|
+
# ContentType.LEFT_CHAT_MEMBER,
|
|
16
|
+
# ContentType.SUPERGROUP_CHAT_CREATED,
|
|
17
|
+
# ContentType.NEW_CHAT_PHOTO,
|
|
18
|
+
# ContentType.FORUM_TOPIC_CREATED,
|
|
19
|
+
# ContentType.FORUM_TOPIC_EDITED,
|
|
20
|
+
ContentType.FORUM_TOPIC_CLOSED,
|
|
21
|
+
# ContentType.GENERAL_FORUM_TOPIC_HIDDEN, # deletable
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
async def del_cbq(msg: Message):
|
|
26
|
+
try:
|
|
27
|
+
await msg.delete()
|
|
28
|
+
logging.info({"DELETED": msg.model_dump(exclude_none=True)})
|
|
29
|
+
except TelegramBadRequest:
|
|
30
|
+
logging.error({"NOT_DELETED": msg.model_dump(exclude_none=True)})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@last.message()
|
|
34
|
+
async def all_rest(msg: Message):
|
|
35
|
+
logging.warning(
|
|
36
|
+
{
|
|
37
|
+
"NO_HANDLED": msg.model_dump(
|
|
38
|
+
exclude_none=True,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from aiogram import Router, F
|
|
2
|
+
from aiogram.exceptions import TelegramBadRequest
|
|
3
|
+
from aiogram.filters import Command
|
|
4
|
+
from aiogram.fsm.context import FSMContext
|
|
5
|
+
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
|
6
|
+
from xync_schema import models
|
|
7
|
+
from xync_schema.enums import SynonymType
|
|
8
|
+
|
|
9
|
+
from xync_bot.routers.cond.func import wrap_cond, get_val, btns, rkm, ikm, SynTypeCd, CondCd
|
|
10
|
+
|
|
11
|
+
cr = Router(name="cond")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@cr.message(Command("cond"))
|
|
15
|
+
async def start(msg: Message, state: FSMContext):
|
|
16
|
+
await msg.reply('Заполняем синонимы признаков условий объявлений: "ссылка на инструкцию"', reply_markup=rkm)
|
|
17
|
+
await show_new_cond(msg, state)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def show_new_cond(msg: Message, state: FSMContext):
|
|
21
|
+
cond = await models.Cond.filter(parsed__isnull=True).order_by("-created_at").first().prefetch_related("parsed")
|
|
22
|
+
await state.set_data({"cond": cond})
|
|
23
|
+
await show_cond(msg, cond)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def show_cond(msg: Message, cond: models.Cond):
|
|
27
|
+
await msg.answer(await wrap_cond(cond), reply_markup=ikm)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@cr.message(F.quote)
|
|
31
|
+
async def got_synonym(msg: Message, state: FSMContext):
|
|
32
|
+
if not (msg.text in {st.name for st in SynonymType} and SynonymType[msg.text]):
|
|
33
|
+
return await msg.reply_text(
|
|
34
|
+
f'Нет раздела "{msg.text}", не пиши текст сам, выдели кусок из моего сообщения,'
|
|
35
|
+
f"ответь на него, выбери кнопку раздела"
|
|
36
|
+
)
|
|
37
|
+
if not msg.quote:
|
|
38
|
+
return await msg.reply_text(f"Вы забыли выделить кусок текста для {msg.text}")
|
|
39
|
+
if typ := SynonymType[msg.text]:
|
|
40
|
+
await state.update_data({"syntext": msg.quote.text, "cmsg": msg.reply_to_message})
|
|
41
|
+
await models.Synonym.update_or_create({"typ": typ}, txt=msg.quote.text)
|
|
42
|
+
if rm := await btns(typ, msg.quote.text):
|
|
43
|
+
return await msg.answer("Уточните", reply_markup=rm, reply_to_message_id=msg.message_id)
|
|
44
|
+
await syn_result(msg, SynTypeCd(typ=typ.name, val=1), state)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@cr.callback_query(SynTypeCd.filter())
|
|
49
|
+
async def got_synonym_val(cbq: CallbackQuery, callback_data: SynTypeCd, state: FSMContext):
|
|
50
|
+
await syn_result(cbq.message, callback_data, state)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def syn_result(msg: Message, cbd: SynTypeCd, state: FSMContext):
|
|
54
|
+
cond: models.Cond = await state.get_value("cond")
|
|
55
|
+
typ = SynonymType[cbd.typ]
|
|
56
|
+
val, hval = await get_val(typ, cbd.val)
|
|
57
|
+
syntext = await state.get_value("syntext")
|
|
58
|
+
syn, _ = await models.Synonym.update_or_create({"val": val}, typ=typ, txt=syntext)
|
|
59
|
+
await models.CondParsed.update_or_create({typ.name: val}, cond_id=cond.id)
|
|
60
|
+
await msg.answer(
|
|
61
|
+
f'Текст "{syntext}" определен как синоним для `{typ.name}` со значением {hval}',
|
|
62
|
+
reply_markup=InlineKeyboardMarkup(
|
|
63
|
+
inline_keyboard=[
|
|
64
|
+
[
|
|
65
|
+
InlineKeyboardButton(text="Готово! Давай новый", callback_data="cond:complete"),
|
|
66
|
+
InlineKeyboardButton(text="Продолжить с этим текстом", callback_data="cond:continue"),
|
|
67
|
+
]
|
|
68
|
+
]
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@cr.callback_query(CondCd.filter())
|
|
74
|
+
async def got_action(cbq: CallbackQuery, callback_data: CondCd, state: FSMContext):
|
|
75
|
+
cond: models.Cond = await state.get_value("cond")
|
|
76
|
+
if callback_data.act == "complete":
|
|
77
|
+
await models.CondParsed.update_or_create({"parsed": True}, cond=cond)
|
|
78
|
+
await show_new_cond(cbq.message, state)
|
|
79
|
+
elif callback_data.act == "pass":
|
|
80
|
+
await show_new_cond(cbq.message, state)
|
|
81
|
+
elif callback_data.act == "refresh":
|
|
82
|
+
try:
|
|
83
|
+
await cbq.message.edit_text(await wrap_cond(cond), reply_markup=ikm)
|
|
84
|
+
except TelegramBadRequest as e:
|
|
85
|
+
if "message is not modified:" in e.message:
|
|
86
|
+
return await cbq.answer("Текст не изменился")
|
|
87
|
+
raise e
|
|
88
|
+
elif callback_data.act == "continue":
|
|
89
|
+
await (await state.get_value("cmsg")).delete()
|
|
90
|
+
await show_cond(cbq.message, cond)
|
|
91
|
+
return await cbq.answer(callback_data.act)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from inspect import isclass
|
|
4
|
+
from typing import Coroutine
|
|
5
|
+
|
|
6
|
+
from aiogram.filters.callback_data import CallbackData
|
|
7
|
+
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
|
|
8
|
+
from cyrtranslit import to_latin
|
|
9
|
+
from xync_schema import models
|
|
10
|
+
from xync_schema.enums import SynonymType, Boundary, Party, Slip, AbuserType, NameType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SynTypeCd(CallbackData, prefix="st"):
|
|
14
|
+
typ: str
|
|
15
|
+
val: int
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CondCd(CallbackData, prefix="cond"):
|
|
19
|
+
act: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def wrap_cond(cond: models.Cond):
|
|
23
|
+
bnks = await models.Synonym.filter(typ=SynonymType.bank)
|
|
24
|
+
bnks = "|".join("\\b" + b.txt + ("\\b" if b.boundary & Boundary.right else "") for b in bnks)
|
|
25
|
+
ad = await models.Ad.filter(cond=cond).order_by("-updated_at").prefetch_related("ex", "pair_side").first()
|
|
26
|
+
hdr = ("SELL" if ad.pair_side.is_sell else "BUY") + f":{ad.price} [{ad.min_fiat}]"
|
|
27
|
+
if (mx := (ad.max_fiat or ad.amount)) > ad.min_fiat:
|
|
28
|
+
hdr += f"->[{mx}]"
|
|
29
|
+
txt = cond.raw_txt
|
|
30
|
+
for syn in await models.Synonym.all():
|
|
31
|
+
lb, rb = "\\b" if syn.boundary & Boundary.left else "", "\\b" if syn.boundary & Boundary.right else ""
|
|
32
|
+
if syn.typ == SynonymType.bank_side:
|
|
33
|
+
syn.txt.replace("#banks#", f"({bnks})")
|
|
34
|
+
if syn.is_re or syn.txt in txt:
|
|
35
|
+
pattern = re.compile(lb + syn.txt + rb)
|
|
36
|
+
if match := re.search(pattern, txt):
|
|
37
|
+
g = match.group()
|
|
38
|
+
val, hval = await get_val(syn.typ, syn.val)
|
|
39
|
+
val = syn.typ.name + (f'="{hval}"' if hval else "")
|
|
40
|
+
txt = re.sub(pattern, f"<code>{g}</code><tg-spoiler>[{val}]</tg-spoiler>", txt)
|
|
41
|
+
return f"<blockquote>{hdr} {ad.ex.name}</blockquote>{txt}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def cbanks(bnid: str) -> list[tuple[int, str]]:
|
|
45
|
+
beginning = to_latin(bnid[:2], lang_code="ru")
|
|
46
|
+
return await models.Pm.filter(norm__startswith=beginning, bank=True).values_list("id", "norm")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def cppo(txt: str) -> list[tuple[int, str]]:
|
|
50
|
+
opts = re.findall(r"\d+", txt) or [1, 1000, 5000]
|
|
51
|
+
return [(o, str(o)) for o in opts]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def contact(txt: str) -> list[tuple[int, str]]: ...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
synopts: dict[SynonymType, list[str] | type(IntEnum) | None | Coroutine] = {
|
|
58
|
+
SynonymType.name: ["not_slavic", "slavic"],
|
|
59
|
+
SynonymType.ppo: cppo,
|
|
60
|
+
SynonymType.from_party: Party,
|
|
61
|
+
SynonymType.to_party: Party,
|
|
62
|
+
SynonymType.slip_req: Slip,
|
|
63
|
+
SynonymType.slip_send: Slip,
|
|
64
|
+
SynonymType.abuser: AbuserType,
|
|
65
|
+
SynonymType.scale: ["1", "100", "1000", "5000"],
|
|
66
|
+
SynonymType.slavic: NameType,
|
|
67
|
+
SynonymType.mtl_like: None,
|
|
68
|
+
SynonymType.bank: cbanks,
|
|
69
|
+
SynonymType.bank_side: ["except", "only"],
|
|
70
|
+
SynonymType.sbp_strict: ["no", "sbp", "card"],
|
|
71
|
+
SynonymType.contact: contact,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
rkm = ReplyKeyboardMarkup(
|
|
75
|
+
keyboard=[
|
|
76
|
+
[KeyboardButton(text="ppo"), KeyboardButton(text="abuser")],
|
|
77
|
+
[KeyboardButton(text="from_party"), KeyboardButton(text="to_party")],
|
|
78
|
+
[KeyboardButton(text="slip_send"), KeyboardButton(text="slip_req")],
|
|
79
|
+
[KeyboardButton(text="name"), KeyboardButton(text="slavic")],
|
|
80
|
+
[KeyboardButton(text="scale"), KeyboardButton(text="mtl_like")],
|
|
81
|
+
[KeyboardButton(text="bank"), KeyboardButton(text="bank_side")],
|
|
82
|
+
[KeyboardButton(text="sbp_strict"), KeyboardButton(text="contact")],
|
|
83
|
+
],
|
|
84
|
+
# one_time_keyboard=True,
|
|
85
|
+
)
|
|
86
|
+
ikm = InlineKeyboardMarkup(
|
|
87
|
+
inline_keyboard=[
|
|
88
|
+
[
|
|
89
|
+
InlineKeyboardButton(text="Обновить", callback_data="cond:refresh"),
|
|
90
|
+
InlineKeyboardButton(text="Пропустить", callback_data="cond:pass"),
|
|
91
|
+
]
|
|
92
|
+
]
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def get_val(typ: SynonymType.__class__, val: str | int) -> tuple[SynonymType | int | bool, str]:
|
|
97
|
+
if isinstance(val, str) and val.isnumeric():
|
|
98
|
+
val = int(val)
|
|
99
|
+
if isclass(lst := synopts[typ]) and issubclass(lst, IntEnum):
|
|
100
|
+
return (v := lst(val)), v.name
|
|
101
|
+
elif isinstance(lst, list):
|
|
102
|
+
return val, lst[val]
|
|
103
|
+
elif typ == SynonymType.bank:
|
|
104
|
+
return val, (await models.Pm[val]).norm
|
|
105
|
+
return val, val
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def btns(typ: SynonymType.__class__, txt: str = None) -> InlineKeyboardMarkup | None:
|
|
109
|
+
if lst := synopts[typ]:
|
|
110
|
+
if isinstance(lst, list):
|
|
111
|
+
kb = [[InlineKeyboardButton(text=n, callback_data=f"st:{typ.name}:{i}")] for i, n in enumerate(lst)]
|
|
112
|
+
elif isclass(lst) and issubclass(lst, IntEnum):
|
|
113
|
+
kb = [[InlineKeyboardButton(text=i.name, callback_data=f"st:{typ.name}:{i.value}")] for i in lst]
|
|
114
|
+
else:
|
|
115
|
+
kb = [[InlineKeyboardButton(text=n, callback_data=f"st:{typ.name}:{i}")] for i, n in await lst(txt)]
|
|
116
|
+
return InlineKeyboardMarkup(inline_keyboard=kb)
|
|
117
|
+
else:
|
|
118
|
+
return lst
|
xync_bot-0.3.7.dev1/xync_bot/routers/main.py → xync_bot-0.3.24/xync_bot/routers/main/handler.py
RENAMED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from aiogram import Router, F
|
|
4
|
-
from aiogram.enums import ContentType
|
|
5
|
-
from aiogram.exceptions import TelegramBadRequest
|
|
6
4
|
from aiogram.filters import CommandStart, CommandObject, ChatMemberUpdatedFilter, JOIN_TRANSITION, LEAVE_TRANSITION
|
|
7
5
|
from aiogram.filters.callback_data import CallbackData
|
|
8
6
|
from aiogram.types import (
|
|
9
|
-
User
|
|
7
|
+
User,
|
|
10
8
|
ChatMemberUpdated,
|
|
11
9
|
Message,
|
|
12
10
|
InlineKeyboardMarkup,
|
|
@@ -15,12 +13,12 @@ from aiogram.types import (
|
|
|
15
13
|
WebAppInfo,
|
|
16
14
|
)
|
|
17
15
|
from aiogram.utils.deep_linking import create_start_link
|
|
18
|
-
from xync_schema
|
|
16
|
+
from xync_schema import models
|
|
19
17
|
|
|
20
|
-
from xync_bot.routers import user_upsert
|
|
21
18
|
from xync_bot.shared import NavCallbackData
|
|
19
|
+
from xync_bot.store import Store
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
mr = Router(name="main")
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
|
|
@@ -31,6 +29,7 @@ class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
|
|
|
31
29
|
home_btns = InlineKeyboardMarkup(
|
|
32
30
|
inline_keyboard=[
|
|
33
31
|
[
|
|
32
|
+
InlineKeyboardButton(text="Transfer", callback_data=NavCallbackData(to="transfer").pack()),
|
|
34
33
|
InlineKeyboardButton(text="Invite", callback_data=NavCallbackData(to="ref_link").pack()),
|
|
35
34
|
InlineKeyboardButton(text="Get VPN", callback_data=NavCallbackData(to="get_vpn").pack()),
|
|
36
35
|
]
|
|
@@ -38,19 +37,19 @@ home_btns = InlineKeyboardMarkup(
|
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
|
|
41
|
-
@
|
|
40
|
+
@mr.message(CommandStart(deep_link=True, deep_link_encoded=True))
|
|
42
41
|
async def start_handler(msg: Message, command: CommandObject):
|
|
43
|
-
me:
|
|
42
|
+
me: User = msg.from_user
|
|
44
43
|
ref_id: int = command.args.isnumeric() and int(command.args)
|
|
45
|
-
user = await User.get_or_none(id=me.id, blocked=False)
|
|
44
|
+
user = await models.User.get_or_none(id=me.id, blocked=False)
|
|
46
45
|
rm = None
|
|
47
46
|
logging.info(msg, {"src": "start"})
|
|
48
47
|
if user:
|
|
49
48
|
rs, rm = f"{me.full_name}, you have registered already😉", home_btns
|
|
50
|
-
elif not (ref := await User.get_or_none(id=ref_id)):
|
|
49
|
+
elif not (ref := await models.User.get_or_none(id=ref_id)):
|
|
51
50
|
rs = f"No registered user #{ref_id}😬"
|
|
52
51
|
else: # new user created
|
|
53
|
-
user, cr = await
|
|
52
|
+
user, cr = await models.User.tg2in(me, False)
|
|
54
53
|
await user.update_from_dict({"ref": ref}).save()
|
|
55
54
|
approve_btns = InlineKeyboardMarkup(
|
|
56
55
|
inline_keyboard=[
|
|
@@ -67,36 +66,20 @@ async def start_handler(msg: Message, command: CommandObject):
|
|
|
67
66
|
return await msg.answer(rs, reply_markup=rm)
|
|
68
67
|
|
|
69
68
|
|
|
70
|
-
@
|
|
71
|
-
async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData) -> None:
|
|
72
|
-
protege = await User[callback_data.to]
|
|
73
|
-
if callback_data.res:
|
|
74
|
-
# protege.status = UserStatus.RESTRICTED
|
|
75
|
-
await protege.save()
|
|
76
|
-
rs = f"{cb.from_user.full_name}, теперь Вы несете ответветвенность за {protege.username}"
|
|
77
|
-
else:
|
|
78
|
-
rs = f'Вы отклонили запрос юзера "{protege.username}" на Вашу протекцию'
|
|
79
|
-
res = {True: "одобрил", False: "отклонил"}
|
|
80
|
-
txt = f"{cb.from_user.full_name} {res[callback_data.res]} вашу регистрацию"
|
|
81
|
-
txt, rm = (f"Поздравляем! {txt}💥", home_btns) if callback_data.res else (f"К сожалению {txt}😢", None)
|
|
82
|
-
await cb.bot.send_message(protege.id, txt, reply_markup=rm)
|
|
83
|
-
await cb.answer("👌🏼")
|
|
84
|
-
await cb.message.edit_text(rs)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@main.message(CommandStart(deep_link=True)) # attempt to reg by fake link
|
|
69
|
+
@mr.message(CommandStart(deep_link=True)) # attempt to reg by fake link
|
|
88
70
|
async def fraud_handler(msg: Message):
|
|
89
71
|
logging.warning(f"Start: {msg.from_user.id}. Msg: {msg}")
|
|
90
72
|
# todo: alert to admins! Fraud attempt!
|
|
91
73
|
await msg.answer("🤔")
|
|
92
74
|
|
|
93
75
|
|
|
94
|
-
@
|
|
95
|
-
async def
|
|
76
|
+
@mr.message(CommandStart()) # обычный /start
|
|
77
|
+
async def home(msg: Message, store: Store):
|
|
96
78
|
me = msg.from_user
|
|
97
|
-
user,
|
|
79
|
+
user, is_new = await models.User.tg_upsert(me, False)
|
|
80
|
+
|
|
98
81
|
rr = "сначала вы должны найти поручителя, и перейти по его реферальной ссылке.\nhttps://telegra.ph/XyncNet-02-13"
|
|
99
|
-
if
|
|
82
|
+
if is_new: # has ref and created now
|
|
100
83
|
await msg.answer(f"Здравствуйте {me.full_name}, что бы использовать возможности нашей сети, {rr}")
|
|
101
84
|
elif not user.ref_id:
|
|
102
85
|
await msg.answer(rr.capitalize())
|
|
@@ -104,10 +87,27 @@ async def start_no_ref_handler(msg: Message):
|
|
|
104
87
|
await msg.answer(f"{me.full_name}, не балуйтесь, вы и так уже активный участник👌🏼", reply_markup=home_btns)
|
|
105
88
|
|
|
106
89
|
|
|
107
|
-
@
|
|
90
|
+
@mr.callback_query(RrCallbackData.filter())
|
|
91
|
+
async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData) -> None:
|
|
92
|
+
protege = await models.User[callback_data.to]
|
|
93
|
+
if callback_data.res:
|
|
94
|
+
# protege.status = UserStatus.RESTRICTED
|
|
95
|
+
await protege.save()
|
|
96
|
+
rs = f"{cb.from_user.full_name}, теперь Вы несете ответветвенность за {protege.username}"
|
|
97
|
+
else:
|
|
98
|
+
rs = f'Вы отклонили запрос юзера "{protege.username}" на Вашу протекцию'
|
|
99
|
+
res = {True: "одобрил", False: "отклонил"}
|
|
100
|
+
txt = f"{cb.from_user.full_name} {res[callback_data.res]} вашу регистрацию"
|
|
101
|
+
txt, rm = (f"Поздравляем! {txt}💥", home_btns) if callback_data.res else (f"К сожалению {txt}😢", None)
|
|
102
|
+
await cb.bot.send_message(protege.id, txt, reply_markup=rm)
|
|
103
|
+
await cb.answer("👌🏼")
|
|
104
|
+
await cb.message.edit_text(rs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@mr.callback_query(NavCallbackData.filter(F.to.__eq__("ref_link")))
|
|
108
108
|
async def ref_link_handler(cbq: CallbackQuery):
|
|
109
109
|
me = cbq.from_user
|
|
110
|
-
if not (u := await User.get_or_none(id=me.id, blocked=False).prefetch_related("ref")):
|
|
110
|
+
if not (u := await models.User.get_or_none(id=me.id, blocked=False).prefetch_related("ref")):
|
|
111
111
|
return await cbq.answer(f"{me.full_name}, сначала сами получите одобрение поручителя😉")
|
|
112
112
|
link = await create_start_link(cbq.bot, str(u.id), encode=True)
|
|
113
113
|
logging.info(f"Start: {me.id}. Msg: {cbq}")
|
|
@@ -119,36 +119,36 @@ async def ref_link_handler(cbq: CallbackQuery):
|
|
|
119
119
|
await cbq.answer("Wait for your protege request..")
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
@
|
|
122
|
+
@mr.my_chat_member(F.chat.type == "private") # my_chat_member is fired on add bot to any chat. filter for preventing
|
|
123
123
|
async def my_user_set_status(my_chat_member: ChatMemberUpdated):
|
|
124
124
|
logging.info({"my_chat_member": my_chat_member.model_dump(exclude_none=True)})
|
|
125
|
-
u:
|
|
125
|
+
u: User = my_chat_member.from_user
|
|
126
126
|
blocked = my_chat_member.new_chat_member.status in ("left", "kicked")
|
|
127
|
-
await
|
|
127
|
+
await models.User.tg2in(u, blocked)
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
@
|
|
130
|
+
@mr.my_chat_member()
|
|
131
131
|
async def user_set_status(my_chat_member: ChatMemberUpdated):
|
|
132
132
|
if my_chat_member.new_chat_member.user.username == "XyncNetBot": # удалена группа где бот был добавлен админом
|
|
133
|
-
if forum := await Forum.get_or_none(id=my_chat_member.chat.id):
|
|
133
|
+
if forum := await models.Forum.get_or_none(id=my_chat_member.chat.id):
|
|
134
134
|
await forum.delete()
|
|
135
135
|
res = f"I {my_chat_member.new_chat_member.status} from {my_chat_member.chat.id}:{my_chat_member.chat.title}"
|
|
136
136
|
return logging.info(res)
|
|
137
137
|
logging.info({"my_chat_member": my_chat_member.model_dump(exclude_none=True)})
|
|
138
|
-
u:
|
|
138
|
+
u: User = my_chat_member.from_user
|
|
139
139
|
blocked = my_chat_member.new_chat_member.status in ("left", "kicked")
|
|
140
140
|
if blocked:
|
|
141
|
-
if forum := await Forum.get_or_none(id=my_chat_member.chat.id, user_id=u.id):
|
|
141
|
+
if forum := await models.Forum.get_or_none(id=my_chat_member.chat.id, user_id=u.id):
|
|
142
142
|
if forum.joined:
|
|
143
143
|
forum.joined = False
|
|
144
144
|
await forum.save()
|
|
145
|
-
await
|
|
145
|
+
return await models.User.tg2in(u, blocked)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
@
|
|
148
|
+
@mr.chat_member(ChatMemberUpdatedFilter(LEAVE_TRANSITION)) # юзер покинул группу Ордеров
|
|
149
149
|
async def on_user_leave(member: ChatMemberUpdated):
|
|
150
150
|
logging.info({"user_leave": member.model_dump(exclude_none=True)})
|
|
151
|
-
if forum := await Forum[member.chat.id]:
|
|
151
|
+
if forum := await models.Forum[member.chat.id]:
|
|
152
152
|
if forum.joined:
|
|
153
153
|
forum.joined = False
|
|
154
154
|
await forum.save()
|
|
@@ -156,10 +156,10 @@ async def on_user_leave(member: ChatMemberUpdated):
|
|
|
156
156
|
return await member.bot.send_message(member.new_chat_member.user.id, resp)
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
@
|
|
159
|
+
@mr.chat_member(ChatMemberUpdatedFilter(JOIN_TRANSITION)) # Юзер добавился в группу Ордеров
|
|
160
160
|
async def on_user_join(member: ChatMemberUpdated, app_url: str):
|
|
161
161
|
logging.info({"user_join": member.model_dump(exclude_none=True)})
|
|
162
|
-
if forum := await Forum.get_or_none(id=member.chat.id):
|
|
162
|
+
if forum := await models.Forum.get_or_none(id=member.chat.id):
|
|
163
163
|
if not forum.joined:
|
|
164
164
|
forum.joined = True
|
|
165
165
|
await forum.save()
|
|
@@ -170,53 +170,20 @@ async def on_user_join(member: ChatMemberUpdated, app_url: str):
|
|
|
170
170
|
return await member.bot.send_message(member.new_chat_member.user.id, resp, reply_markup=rm)
|
|
171
171
|
|
|
172
172
|
|
|
173
|
-
@
|
|
173
|
+
@mr.message(F.is_topic_message)
|
|
174
174
|
async def order_msg(msg: Message):
|
|
175
|
-
sender = await User[msg.from_user.id]
|
|
175
|
+
sender = await models.User[msg.from_user.id]
|
|
176
176
|
cid = msg.chat.shifted_id
|
|
177
177
|
assert sender.forum == cid, "sender is not client"
|
|
178
|
-
if order := await Order.get_or_none(taker__user_id=sender.id, taker_topic=msg.message_thread_id):
|
|
178
|
+
if order := await models.Order.get_or_none(taker__user_id=sender.id, taker_topic=msg.message_thread_id):
|
|
179
179
|
is_taker = True
|
|
180
|
-
elif order := await Order.get_or_none(ad__agent__user_id=sender.id, maker_topic=msg.message_thread_id):
|
|
180
|
+
elif order := await models.Order.get_or_none(ad__agent__user_id=sender.id, maker_topic=msg.message_thread_id):
|
|
181
181
|
is_taker = False
|
|
182
182
|
else:
|
|
183
183
|
return await msg.answer("No such order")
|
|
184
184
|
# raise Exception("No such order")
|
|
185
|
-
receiver: User = await (order.ad.maker.user if is_taker else order.taker.user)
|
|
185
|
+
receiver: models.User = await (order.ad.maker.user if is_taker else order.taker.user)
|
|
186
186
|
rcv_topic = order.taker_topic if is_taker else order.maker_topic
|
|
187
|
-
await Msg.create(tgid=msg.message_id, txt=msg.text, order_id=order.id, receiver=receiver)
|
|
187
|
+
await models.Msg.create(tgid=msg.message_id, txt=msg.text, order_id=order.id, receiver=receiver)
|
|
188
188
|
logging.info(msg, {"src": "order_msg"})
|
|
189
189
|
return await msg.send_copy(receiver.forum, message_thread_id=rcv_topic)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@main.message(
|
|
193
|
-
F.content_type.not_in(
|
|
194
|
-
{
|
|
195
|
-
ContentType.NEW_CHAT_MEMBERS,
|
|
196
|
-
# ContentType.LEFT_CHAT_MEMBER,
|
|
197
|
-
# ContentType.SUPERGROUP_CHAT_CREATED,
|
|
198
|
-
# ContentType.NEW_CHAT_PHOTO,
|
|
199
|
-
# ContentType.FORUM_TOPIC_CREATED,
|
|
200
|
-
# ContentType.FORUM_TOPIC_EDITED,
|
|
201
|
-
ContentType.FORUM_TOPIC_CLOSED,
|
|
202
|
-
# ContentType.GENERAL_FORUM_TOPIC_HIDDEN, # deletable
|
|
203
|
-
}
|
|
204
|
-
)
|
|
205
|
-
)
|
|
206
|
-
async def del_cbq(msg: Message):
|
|
207
|
-
try:
|
|
208
|
-
await msg.delete()
|
|
209
|
-
logging.info({"DELETED": msg.model_dump(exclude_none=True)})
|
|
210
|
-
except TelegramBadRequest:
|
|
211
|
-
logging.error({"NOT_DELETED": msg.model_dump(exclude_none=True)})
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@main.message()
|
|
215
|
-
async def all_rest(msg: Message):
|
|
216
|
-
logging.warning(
|
|
217
|
-
{
|
|
218
|
-
"NO_HANDLED": msg.model_dump(
|
|
219
|
-
exclude_none=True,
|
|
220
|
-
)
|
|
221
|
-
}
|
|
222
|
-
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from aiogram.filters.callback_data import CallbackData
|
|
2
|
+
|
|
3
|
+
from xync_bot.routers.pay.dep import PayStep, ActionType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MoneyType(CallbackData, prefix="target"):
|
|
7
|
+
is_fiat: int # bool
|
|
8
|
+
is_target: int # bool
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Cur(CallbackData, prefix="cur"):
|
|
12
|
+
id: int
|
|
13
|
+
is_target: int # bool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Coin(CallbackData, prefix="coin"):
|
|
17
|
+
id: int
|
|
18
|
+
is_target: int # bool
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Cred(CallbackData, prefix="cred"):
|
|
22
|
+
id: int
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Ex(CallbackData, prefix="ex"):
|
|
26
|
+
id: int
|
|
27
|
+
is_target: int # bool
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Pm(CallbackData, prefix="pm"):
|
|
31
|
+
pmcur_id: int
|
|
32
|
+
is_target: bool
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Ppo(CallbackData, prefix="ppo"):
|
|
36
|
+
num: int
|
|
37
|
+
is_target: int # bool
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PayNav(CallbackData, prefix="pay_nav"):
|
|
41
|
+
to: PayStep
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Time(CallbackData, prefix="time"):
|
|
45
|
+
minutes: int # время в минутах
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Action(CallbackData, prefix="action"):
|
|
49
|
+
act: ActionType # "received" или "not_received"
|