xync-bot 0.3.1__tar.gz → 0.3.3__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.

@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: xync-bot
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Telegram bot with web app for xync net
5
5
  Author-email: Artemiev <mixartemev@gmail.com>
6
6
  License: EULA
@@ -0,0 +1,8 @@
1
+ from aiogram.types import User
2
+ from aiogram.utils.web_app import WebAppUser
3
+ from xync_schema import models
4
+
5
+
6
+ async def user_upsert(u: User | WebAppUser, blocked: bool = None) -> tuple[User, bool]:
7
+ user_in: models.User.Upd = await models.User.tg2in(u, blocked)
8
+ return await models.User.update_or_create(user_in.model_dump(exclude_none=True, exclude={"id"}), id=u.id)
@@ -0,0 +1,244 @@
1
+ import logging
2
+ from os import path
3
+
4
+ from aiogram import Router, F
5
+ from aiogram.enums import ContentType
6
+ from aiogram.exceptions import TelegramBadRequest
7
+ from aiogram.filters import CommandStart, CommandObject, ChatMemberUpdatedFilter, JOIN_TRANSITION, LEAVE_TRANSITION
8
+ from aiogram.filters.callback_data import CallbackData
9
+ from aiogram.types import (
10
+ User as TgUser,
11
+ ChatMemberUpdated,
12
+ Message,
13
+ InlineKeyboardMarkup,
14
+ InlineKeyboardButton,
15
+ CallbackQuery,
16
+ ChatJoinRequest,
17
+ WebAppInfo,
18
+ FSInputFile,
19
+ )
20
+ from aiogram.utils.deep_linking import create_start_link
21
+ from xync_bot.handlers import user_upsert
22
+ from xync_schema.models import User, Order, Msg, Forum
23
+
24
+ from xync_bot.shared import NavCallbackData
25
+
26
+ main = Router()
27
+
28
+
29
+ class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
30
+ to: int
31
+ res: bool
32
+
33
+
34
+ home_btns = InlineKeyboardMarkup(
35
+ inline_keyboard=[
36
+ [
37
+ InlineKeyboardButton(text="Invite", callback_data=NavCallbackData(to="ref_link").pack()),
38
+ InlineKeyboardButton(text="Get VPN", callback_data=NavCallbackData(to="get_vpn").pack()),
39
+ ]
40
+ ]
41
+ )
42
+
43
+
44
+ @main.message(CommandStart(deep_link=True, deep_link_encoded=True))
45
+ async def start_handler(msg: Message, command: CommandObject):
46
+ me: TgUser = msg.from_user
47
+ ref_id: int = command.args.isnumeric() and int(command.args)
48
+ user = await User.get_or_none(id=me.id, blocked=False)
49
+ rm = None
50
+ logging.info(msg, {"src": "start"})
51
+ if user:
52
+ rs, rm = f"{me.full_name}, you have registered already😉", home_btns
53
+ elif not (ref := await User.get_or_none(id=ref_id)):
54
+ rs = f"No registered user #{ref_id}😬"
55
+ else: # new user created
56
+ user, cr = await user_upsert(me, False)
57
+ await user.update_from_dict({"ref": ref}).save()
58
+ approve_btns = InlineKeyboardMarkup(
59
+ inline_keyboard=[
60
+ [
61
+ InlineKeyboardButton(text="Отклонить", callback_data=RrCallbackData(to=user.id, res=False).pack()),
62
+ InlineKeyboardButton(text="Одобрить", callback_data=RrCallbackData(to=user.id, res=True).pack()),
63
+ ]
64
+ ]
65
+ )
66
+ await msg.bot.send_message(
67
+ ref.id, f"{me.full_name} просит что б Вы взяли за него/ее ответственность", reply_markup=approve_btns
68
+ )
69
+ return await msg.answer(f"Please wait for @{ref.username} approving...")
70
+ return await msg.answer(rs, reply_markup=rm)
71
+
72
+
73
+ @main.callback_query(RrCallbackData.filter())
74
+ async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData) -> None:
75
+ protege = await User[callback_data.to]
76
+ if callback_data.res:
77
+ # protege.status = UserStatus.RESTRICTED
78
+ await protege.save()
79
+ rs = f"{cb.from_user.full_name}, теперь Вы несете ответветвенность за {protege.username}"
80
+ else:
81
+ rs = f'Вы отклонили запрос юзера "{protege.username}" на Вашу протекцию'
82
+ res = {True: "одобрил", False: "отклонил"}
83
+ txt = f"{cb.from_user.full_name} {res[callback_data.res]} вашу регистрацию"
84
+ txt, rm = (f"Поздравляем! {txt}💥", home_btns) if callback_data.res else (f"К сожалению {txt}😢", None)
85
+ await cb.bot.send_message(protege.id, txt, reply_markup=rm)
86
+ await cb.answer("👌🏼")
87
+ await cb.message.edit_text(rs)
88
+
89
+
90
+ @main.message(CommandStart(deep_link=True)) # attempt to reg by fake link
91
+ async def fraud_handler(msg: Message):
92
+ logging.warning(f"Start: {msg.from_user.id}. Msg: {msg}")
93
+ # todo: alert to admins! Fraud attempt!
94
+ await msg.answer("🤔")
95
+
96
+
97
+ @main.message(CommandStart())
98
+ async def start_no_ref_handler(msg: Message):
99
+ me = msg.from_user
100
+ user, cr = await user_upsert(me, False)
101
+ rr = "сначала вы должны найти поручителя, и перейти по его реферальной ссылке.\nhttps://telegra.ph/XyncNet-02-13"
102
+ if cr: # has ref and created now
103
+ await msg.answer(f"Здравствуйте {me.full_name}, что бы использовать возможности нашей сети, {rr}")
104
+ elif not user.ref_id:
105
+ await msg.answer(rr.capitalize())
106
+ else:
107
+ await msg.answer(f"{me.full_name}, не балуйтесь, вы и так уже активный участник👌🏼", reply_markup=home_btns)
108
+
109
+
110
+ @main.callback_query(NavCallbackData.filter(F.to == "ref_link"))
111
+ async def ref_link_handler(cbq: CallbackQuery):
112
+ me = cbq.from_user
113
+ if not (u := await User.get_or_none(id=me.id, blocked=False).prefetch_related("ref")):
114
+ return await cbq.answer(f"{me.full_name}, сначала сами получите одобрение поручителя😉")
115
+ link = await create_start_link(cbq.bot, str(u.id), encode=True)
116
+ logging.info(f"Start: {me.id}. Msg: {cbq}")
117
+ await cbq.message.answer(
118
+ f"Your referrer is {u.ref_id and u.ref.username}"
119
+ f"\nThis is your invite link: {link}"
120
+ f"\nGive it to your protege, and approve his request"
121
+ )
122
+ await cbq.answer("Wait for your protege request..")
123
+
124
+
125
+ @main.my_chat_member(F.chat.type == "private") # my_chat_member is fired on add bot to any chat. filter for preventing
126
+ async def my_user_set_status(my_chat_member: ChatMemberUpdated):
127
+ logging.info({"my_chat_member": my_chat_member.model_dump(exclude_none=True)})
128
+ u: TgUser = my_chat_member.from_user
129
+ blocked = my_chat_member.new_chat_member.status in ("left", "kicked")
130
+ await user_upsert(u, blocked)
131
+
132
+
133
+ @main.my_chat_member()
134
+ async def user_set_status(my_chat_member: ChatMemberUpdated):
135
+ logging.info({"my_chat_member": my_chat_member.model_dump(exclude_none=True)})
136
+ u: TgUser = my_chat_member.from_user
137
+ blocked = my_chat_member.new_chat_member.status in ("left", "kicked")
138
+ await user_upsert(u, blocked)
139
+
140
+
141
+ @main.chat_member(ChatMemberUpdatedFilter(LEAVE_TRANSITION))
142
+ async def on_user_leave(member: ChatMemberUpdated):
143
+ logging.info({"user_leave": member.model_dump(exclude_none=True)})
144
+ forum = await Forum[member.chat.id]
145
+ if not forum.joined:
146
+ resp = (
147
+ f"{member.from_user.username or member.from_user.full_name}#{member.from_user.id} "
148
+ f"already leaved from {member.chat.title}#{member.chat.id}"
149
+ )
150
+ logging.error(resp)
151
+ else:
152
+ forum.joined = False
153
+ await forum.save()
154
+ resp = "Bye!"
155
+ return await member.bot.send_message(member.new_chat_member.user.id, resp)
156
+
157
+
158
+ @main.chat_join_request()
159
+ async def on_join_request(req: ChatJoinRequest):
160
+ logging.info({"join_request": req.model_dump(exclude_none=True)})
161
+ forum = await Forum[req.chat.id]
162
+ if forum.user_id != req.from_user.id:
163
+ resp = f"{req.chat.title} is chat for user#{forum.user_id}"
164
+ logging.error(resp)
165
+ forum.joined = not await req.decline()
166
+ else:
167
+ resp = "Your request approved"
168
+ forum.joined = await req.approve()
169
+ await forum.save()
170
+ return await req.bot.send_message(req.user_chat_id, resp)
171
+
172
+
173
+ @main.chat_member(ChatMemberUpdatedFilter(JOIN_TRANSITION))
174
+ async def on_user_join(member: ChatMemberUpdated):
175
+ logging.info({"user_join": member.model_dump(exclude_none=True)})
176
+ forum = await Forum[member.chat.id]
177
+ rm = None
178
+ if forum.user_id != member.new_chat_member.user.id:
179
+ if member.new_chat_member.user.id in (6806432376, forum.created_by_id): # 6806432376=xyncNetBot
180
+ return
181
+ resp = f"{member.chat.title} is chat for user#{forum.user_id}"
182
+ logging.error(resp)
183
+ await member.bot.ban_chat_member(member.chat.id, member.new_chat_member.user.id)
184
+ else:
185
+ if not (await member.bot.get_chat(member.chat.id)).photo:
186
+ pth = path.join(path.dirname(path.abspath(__file__)), "xicon.png")
187
+ await member.chat.set_photo(FSInputFile(pth))
188
+ resp = "Welcome to XyncNetwork"
189
+ rm = InlineKeyboardMarkup(
190
+ inline_keyboard=[[InlineKeyboardButton(text="Go!", web_app=WebAppInfo(url="https://test.xync.net/"))]]
191
+ )
192
+ return await member.bot.send_message(member.new_chat_member.user.id, resp, reply_markup=rm)
193
+
194
+
195
+ @main.message(F.is_topic_message)
196
+ async def order_msg(msg: Message):
197
+ sender = await User[msg.from_user.id]
198
+ cid = msg.chat.shifted_id
199
+ assert sender.forum == cid, "sender is not client"
200
+ if order := await Order.get_or_none(taker__user_id=sender.id, taker_topic=msg.message_thread_id):
201
+ is_taker = True
202
+ elif order := await Order.get_or_none(ad__agent__user_id=sender.id, maker_topic=msg.message_thread_id):
203
+ is_taker = False
204
+ else:
205
+ return await msg.answer("No such order")
206
+ # raise Exception("No such order")
207
+ receiver: User = await (order.ad.maker.user if is_taker else order.taker.user)
208
+ rcv_topic = order.taker_topic if is_taker else order.maker_topic
209
+ await Msg.create(tgid=msg.message_id, txt=msg.text, order_id=order.id, receiver=receiver)
210
+ logging.info(msg, {"src": "order_msg"})
211
+ return await msg.send_copy(receiver.forum, message_thread_id=rcv_topic)
212
+
213
+
214
+ @main.message(
215
+ F.content_type.not_in(
216
+ {
217
+ ContentType.NEW_CHAT_MEMBERS,
218
+ # ContentType.LEFT_CHAT_MEMBER,
219
+ # ContentType.SUPERGROUP_CHAT_CREATED,
220
+ # ContentType.NEW_CHAT_PHOTO,
221
+ # ContentType.FORUM_TOPIC_CREATED,
222
+ # ContentType.FORUM_TOPIC_EDITED,
223
+ ContentType.FORUM_TOPIC_CLOSED,
224
+ # ContentType.GENERAL_FORUM_TOPIC_HIDDEN, # deletable
225
+ }
226
+ )
227
+ )
228
+ async def del_cbq(msg: Message):
229
+ try:
230
+ await msg.delete()
231
+ logging.info({"DELETED": msg.model_dump(exclude_none=True)})
232
+ except TelegramBadRequest:
233
+ logging.error({"NOT_DELETED": msg.model_dump(exclude_none=True)})
234
+
235
+
236
+ @main.message()
237
+ async def all_rest(msg: Message):
238
+ logging.warning(
239
+ {
240
+ "NO_HANDLED": msg.model_dump(
241
+ exclude_none=True,
242
+ )
243
+ }
244
+ )
@@ -0,0 +1,12 @@
1
+ from aiogram import Router, F
2
+ from aiogram.types import Message
3
+ from xync_schema.models import User
4
+
5
+ r = Router()
6
+
7
+
8
+ # @main.message(F.chat.is_forum)
9
+ @r.message(F.is_topic_message)
10
+ async def order_msg(msg: Message):
11
+ await User[msg.from_user.id]
12
+ msg.message_thread_id
@@ -0,0 +1,36 @@
1
+ from aiogram import Router, F
2
+ from aiogram.fsm.context import FSMContext
3
+ from aiogram.fsm.state import State
4
+ from aiogram.types import User as TgUser, Message
5
+ from x_auth.enums import Role
6
+ from xync_schema.models import User, Ex
7
+
8
+ photo = Router()
9
+ exns = State("ex_name")
10
+
11
+
12
+ @photo.message(F.photo | F.sticker)
13
+ async def photo_upload(msg: Message, state: FSMContext):
14
+ u: TgUser = msg.from_user
15
+ if not await User.get_or_none(id=u.id, blocked=False, role=Role.ADMIN):
16
+ return await msg.answer("No user")
17
+ if f := msg.sticker:
18
+ await state.set_data({"fid": f.file_id})
19
+ await state.set_state(exns)
20
+ return await msg.answer("Ex name pls")
21
+ res = await set_ex_name(msg.caption, msg.photo[-1].file_id)
22
+ return await msg.answer(res or "No ex", parse_mode="HTML")
23
+
24
+
25
+ async def set_ex_name(name: str, fid: str) -> str | None:
26
+ if ex := await Ex.get_or_none(name=name):
27
+ ex.logo = fid
28
+ await ex.save()
29
+ return fid
30
+ return None
31
+
32
+
33
+ @photo.message(exns)
34
+ async def ex_name(msg: Message, state: FSMContext):
35
+ res = await set_ex_name(msg.text, await state.get_value("fid"))
36
+ return await msg.answer(res or "No ex" + msg.text, parse_mode="HTML")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: xync-bot
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Telegram bot with web app for xync net
5
5
  Author-email: Artemiev <mixartemev@gmail.com>
6
6
  License: EULA
@@ -6,6 +6,7 @@ pyproject.toml
6
6
  test_main.http
7
7
  xync_bot/__init__.py
8
8
  xync_bot/shared.py
9
+ xync_bot/types.py
9
10
  xync_bot.egg-info/PKG-INFO
10
11
  xync_bot.egg-info/SOURCES.txt
11
12
  xync_bot.egg-info/dependency_links.txt
@@ -13,4 +14,7 @@ xync_bot.egg-info/requires.txt
13
14
  xync_bot.egg-info/top_level.txt
14
15
  xync_bot/handlers/__init__.py
15
16
  xync_bot/handlers/main.py
16
- xync_bot/handlers/vpn.py
17
+ xync_bot/handlers/order.py
18
+ xync_bot/handlers/photo.py
19
+ xync_bot/handlers/vpn.py
20
+ xync_bot/handlers/xicon.png
@@ -1,146 +0,0 @@
1
- import logging
2
- from aiogram import Router, F
3
- from aiogram.filters import CommandStart, CommandObject
4
- from aiogram.filters.callback_data import CallbackData
5
- from aiogram.types import (
6
- User as TgUser,
7
- ChatMemberUpdated,
8
- Message,
9
- InlineKeyboardMarkup,
10
- InlineKeyboardButton,
11
- CallbackQuery,
12
- )
13
- from aiogram.utils.deep_linking import create_start_link
14
- from aiogram.utils.web_app import WebAppUser
15
- from tg_auth import Lang
16
- from xync_schema.models import User, UserStatus
17
-
18
- from xync_bot.shared import NavCallbackData
19
-
20
- main = Router()
21
-
22
-
23
- class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
24
- to: int
25
- res: bool
26
-
27
-
28
- home_btns = InlineKeyboardMarkup(
29
- inline_keyboard=[
30
- [
31
- InlineKeyboardButton(text="Invite", callback_data=NavCallbackData(to="ref_link").pack()),
32
- InlineKeyboardButton(text="Get VPN", callback_data=NavCallbackData(to="get_vpn").pack()),
33
- ]
34
- ]
35
- )
36
-
37
-
38
- @main.message(CommandStart(deep_link=True, deep_link_encoded=True))
39
- async def start_handler(msg: Message, command: CommandObject):
40
- me: TgUser = msg.from_user
41
- ref_id: int = command.args.isnumeric() and int(command.args)
42
- user = await User.get_or_none(id=me.id, status__gte=UserStatus.RESTRICTED)
43
- rm = None
44
- if user:
45
- rs, rm = f"{me.full_name}, you have registered already😉", home_btns
46
- elif not (ref := await User.get_or_none(id=ref_id)):
47
- rs = f"No registered user #{ref_id}😬"
48
- else: # new user created
49
- user, cr = await user_upsert(me)
50
- await user.update_from_dict({"ref": ref}).save()
51
- approve_btns = InlineKeyboardMarkup(
52
- inline_keyboard=[
53
- [
54
- InlineKeyboardButton(text="Отклонить", callback_data=RrCallbackData(to=user.id, res=False).pack()),
55
- InlineKeyboardButton(text="Одобрить", callback_data=RrCallbackData(to=user.id, res=True).pack()),
56
- ]
57
- ]
58
- )
59
- await msg.bot.send_message(
60
- ref.id, f"{me.full_name} просит что б Вы взяли за него/ее ответственность", reply_markup=approve_btns
61
- )
62
- return await msg.answer(f"Please wait for @{ref.username} approving...")
63
- return await msg.answer(rs, reply_markup=rm)
64
-
65
-
66
- @main.callback_query(RrCallbackData.filter())
67
- async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData) -> None:
68
- protege = await User[callback_data.to]
69
- if callback_data.res:
70
- # protege.status = UserStatus.RESTRICTED
71
- await protege.save()
72
- rs = f"{cb.from_user.full_name}, теперь Вы несете ответветвенность за {protege.username}"
73
- else:
74
- rs = f'Вы отклонили запрос юзера "{protege.username}" на Вашу протекцию'
75
- res = {True: "одобрил", False: "отклонил"}
76
- txt = f"{cb.from_user.full_name} {res[callback_data.res]} вашу регистрацию"
77
- txt, rm = (f"Поздравляем! {txt}💥", home_btns) if callback_data.res else (f"К сожалению {txt}😢", None)
78
- await cb.bot.send_message(protege.id, txt, reply_markup=rm)
79
- await cb.answer("👌🏼")
80
- await cb.message.edit_text(rs)
81
-
82
-
83
- @main.message(CommandStart(deep_link=True)) # attempt to reg by fake link
84
- async def fraud_handler(msg: Message):
85
- logging.info(f"Start: {msg.from_user.id}. Msg: {msg}")
86
- # todo: alert to admins! Fraud attempt!
87
- await msg.answer("🤔")
88
-
89
-
90
- @main.message(CommandStart())
91
- async def start_no_ref_handler(msg: Message):
92
- me = msg.from_user
93
- user, cr = await user_upsert(me)
94
- rr = "сначала вы должны найти поручителя, и перейти по его реферальной ссылке.\nhttps://telegra.ph/XyncNet-02-13"
95
- if cr: # has ref and created now
96
- await msg.answer(f"Здравствуйте {me.full_name}, что бы использовать возможности нашей сети, {rr}")
97
- elif not user.ref_id:
98
- await msg.answer(rr.capitalize())
99
- else:
100
- await msg.answer(f"{me.full_name}, не балуйтесь, вы и так уже активный участник👌🏼", reply_markup=home_btns)
101
-
102
-
103
- @main.callback_query(NavCallbackData.filter(F.to == "ref_link"))
104
- async def ref_link_handler(cbq: CallbackQuery):
105
- me = cbq.from_user
106
- if not (u := await User.get_or_none(id=me.id, status__gt=UserStatus.RESTRICTED).prefetch_related("ref")):
107
- return await cbq.answer(f"{me.full_name}, сначала сами получите одобрение поручителя😉")
108
- link = await create_start_link(cbq.bot, str(u.id), encode=True)
109
- logging.info(f"Start: {me.id}. Msg: {cbq}")
110
- await cbq.message.answer(
111
- f"Your referrer is {u.ref_id and u.ref.username}"
112
- f"\nThis is your invite link: {link}"
113
- f"\nGive it to your protege, and approve his request"
114
- )
115
- await cbq.answer("Wait for your protege request..")
116
-
117
-
118
- async def user_upsert(u: TgUser | WebAppUser, status: UserStatus = None) -> (User, bool):
119
- pic = (
120
- (gpp := await u.get_profile_photos(0, 1)).photos and gpp.photos[0][-1].file_unique_id
121
- if type(u) is TgUser
122
- else (u.photo_url[0] if u.photo_url else None)
123
- )
124
- udf = {
125
- "username": u.username,
126
- "first_name": u.first_name,
127
- "last_name": u.last_name,
128
- "status": UserStatus.MEMBER,
129
- "lang": u.language_code and Lang[u.language_code],
130
- "pic": pic,
131
- }
132
- if status:
133
- udf.update({"status": status})
134
- return await User.update_or_create(udf, id=u.id)
135
-
136
-
137
- @main.my_chat_member()
138
- async def user_set_status(my_chat_member: ChatMemberUpdated):
139
- u: TgUser = my_chat_member.from_user
140
- new_status = my_chat_member.new_chat_member.status
141
- await user_upsert(u, status=new_status)
142
-
143
-
144
- @main.message()
145
- async def del_cbq(msg: Message):
146
- await msg.delete()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes