mirror of
https://github.com/openmax-server/server.git
synced 2026-05-22 19:41:41 +03:00
TT: ну вроде шире поддержка, а вообще обратная совместимость с максом клас
This commit is contained in:
@@ -419,6 +419,7 @@ class Tools:
|
|||||||
"attaches": json.loads(row.get("attaches")),
|
"attaches": json.loads(row.get("attaches")),
|
||||||
"elements": json.loads(row.get("elements")),
|
"elements": json.loads(row.get("elements")),
|
||||||
"reactionInfo": {},
|
"reactionInfo": {},
|
||||||
|
"link": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Возвращаем
|
# Возвращаем
|
||||||
|
|||||||
@@ -64,11 +64,13 @@ class HistoryProcessors(BaseProcessor):
|
|||||||
"time": int(row.get("time")),
|
"time": int(row.get("time")),
|
||||||
"type": row.get("type"),
|
"type": row.get("type"),
|
||||||
"sender": row.get("sender"),
|
"sender": row.get("sender"),
|
||||||
|
"cid": int(row.get("cid")),
|
||||||
"text": row.get("text"),
|
"text": row.get("text"),
|
||||||
"attaches": json.loads(row.get("attaches")),
|
"attaches": json.loads(row.get("attaches")),
|
||||||
"elements": json.loads(row.get("elements")),
|
"elements": json.loads(row.get("elements")),
|
||||||
"reactionInfo": {},
|
"reactionInfo": {},
|
||||||
"options": 1,
|
"link": {},
|
||||||
|
#"options": 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
if forward > 0:
|
if forward > 0:
|
||||||
@@ -81,14 +83,17 @@ class HistoryProcessors(BaseProcessor):
|
|||||||
|
|
||||||
for row in result:
|
for row in result:
|
||||||
messages.append({
|
messages.append({
|
||||||
"id": row.get("id"),
|
"id": row.get("id") if self.type == 'mobile' else str(row.get('id')),
|
||||||
"time": int(row.get("time")),
|
"time": int(row.get("time")),
|
||||||
"type": row.get("type"),
|
"type": row.get("type"),
|
||||||
"sender": row.get("sender"),
|
"sender": row.get("sender"),
|
||||||
|
"cid": int(row.get("cid")),
|
||||||
"text": row.get("text"),
|
"text": row.get("text"),
|
||||||
"attaches": json.loads(row.get("attaches")),
|
"attaches": json.loads(row.get("attaches")),
|
||||||
"elements": json.loads(row.get("elements")),
|
"elements": json.loads(row.get("elements")),
|
||||||
"reactionInfo": {}
|
"reactionInfo": {},
|
||||||
|
"link": {}
|
||||||
|
#"options": 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Сортируем сообщения по времени
|
# Сортируем сообщения по времени
|
||||||
|
|||||||
@@ -9,319 +9,12 @@ class TTConfig:
|
|||||||
"googNoiseSuppression": "true",
|
"googNoiseSuppression": "true",
|
||||||
"googHighpassFilter": "false",
|
"googHighpassFilter": "false",
|
||||||
"googTypingNoiseDetection": "false",
|
"googTypingNoiseDetection": "false",
|
||||||
"googAudioNetworkAdaptorConfig": "ChyyARkNCtcjPBUK1yM8GKjDASCw6gEomHUwoJwBCgfKAQQIABAACgvCAQgIqMMBELiRAgosqgEpChEIuBcVzcxMPhjogQIlCtejOxIRCOgHFc3MTD4YsOoBJQrXozsYyAEKC7oBCAiw6gEQoJwB"
|
"googAudioNetworkAdaptorConfig": ""
|
||||||
},
|
},
|
||||||
"a-lte": 24,
|
"a-lte": 24,
|
||||||
"a-wifi": 34,
|
"a-wifi": 34,
|
||||||
"account-removal-enabled": False,
|
"account-removal-enabled": False,
|
||||||
"animated-emojis": {
|
"animated-emojis": {},
|
||||||
"❤️": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/03.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/04.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"👍": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-29lottie/e/16.json"
|
|
||||||
},
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/01_m.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"👎": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/17.json"
|
|
||||||
},
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/02.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🙏": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/04.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-29lottie/e/30_ng.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😘": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/05.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/03.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🔥": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/06.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/10.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😂": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/07.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"👏": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/56.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😮": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/09.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"💋": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/13_v02.json"
|
|
||||||
},
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-01-18lottie/r/kissing2.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🥂": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/20.json"
|
|
||||||
},
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/20.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😳": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/09.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/02.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😔": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/r/11.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/05.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😍": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/07.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😯": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/08.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😉": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/09.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🌺": {
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-03-06lottie/flower.json"
|
|
||||||
},
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/11.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎂": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/14.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"💩": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/15.json"
|
|
||||||
},
|
|
||||||
"reactionAction": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-01-18lottie/r/shit_1.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🐰": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/19.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎅": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/21.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎄": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/23.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎆": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/22.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"❄️": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/25.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎉": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-28lottie/e/12.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🥗": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2022-12-29lottie/e/28.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🧡": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/31.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"💔": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/32.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎁": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/34.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🌹": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/35.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🌸": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/36.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍒": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/37.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🥕": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/39.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍑": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/40.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍋": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/41.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍃": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/42.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😺": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/43.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🐶": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/44.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🐽": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-07-20animojie/45.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"💐": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/46.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🎈": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/47.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍾": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/48.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"⚡": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/49.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"⭐": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/50.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"✨": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/51.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"💃": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/52.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"☀️": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/53.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"👋": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/54.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"☕": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/57.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🙂": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/58.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🤩": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-15animoji/59.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😇": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/60.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"😎": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/61.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"🍎": {
|
|
||||||
"emoji": {
|
|
||||||
"url": "https://st.okcdn.ru/static/messages/2023-08-14animoji/62.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"animated-emojis-limits": {
|
"animated-emojis-limits": {
|
||||||
"low": 5,
|
"low": 5,
|
||||||
"average": 10,
|
"average": 10,
|
||||||
@@ -383,10 +76,10 @@ class TTConfig:
|
|||||||
"image-quality": 0.800000011920929,
|
"image-quality": 0.800000011920929,
|
||||||
"image-size": 40000000,
|
"image-size": 40000000,
|
||||||
"image-width": 1680,
|
"image-width": 1680,
|
||||||
"invite-header": "Приглашение в ТамТам",
|
"invite-header": "",
|
||||||
"invite-link": "https://tt.me/starwear",
|
"invite-link": "",
|
||||||
"invite-long": "Я общаюсь в ТамТам, присоединяйся https://tt.me/starwear",
|
"invite-long": "",
|
||||||
"invite-short": "Привет! Ставь ТамТам! Жду ответа! https://tt.me/starwear",
|
"invite-short": "",
|
||||||
"keep-connection": 2,
|
"keep-connection": 2,
|
||||||
"l10n": False,
|
"l10n": False,
|
||||||
"live-location-enabled": True,
|
"live-location-enabled": True,
|
||||||
@@ -453,25 +146,12 @@ class TTConfig:
|
|||||||
"profiling-enabled": False,
|
"profiling-enabled": False,
|
||||||
"progress-diff-for-notify": 1,
|
"progress-diff-for-notify": 1,
|
||||||
"promo-contact-id": 0,
|
"promo-contact-id": 0,
|
||||||
"promo-recent-contacts": True,
|
"promo-recent-contacts": False,
|
||||||
"promo_contact_label": "Белый Маг",
|
"promo_contact_label": "",
|
||||||
"proxy": "msgproxy.okcdn.ru",
|
"proxy": "",
|
||||||
"proxy-domains": [
|
"proxy-domains": [],
|
||||||
"okcdn.ru",
|
"proxy-exclude": [],
|
||||||
"mycdn.me",
|
"proxy-rotation": False,
|
||||||
"ok.ru",
|
|
||||||
"odnoklassniki.ru",
|
|
||||||
"odkl.ru",
|
|
||||||
"vk.com",
|
|
||||||
"userapi.com",
|
|
||||||
"vkuser.net",
|
|
||||||
"vkusercdn.ru"
|
|
||||||
],
|
|
||||||
"proxy-exclude": [
|
|
||||||
"r.mradx.net",
|
|
||||||
"ad.mail.ru"
|
|
||||||
],
|
|
||||||
"proxy-rotation": True,
|
|
||||||
"push-alert-timeout": 604800,
|
"push-alert-timeout": 604800,
|
||||||
"push-tracking-enabled": True,
|
"push-tracking-enabled": True,
|
||||||
"quick-forward-cases": [],
|
"quick-forward-cases": [],
|
||||||
@@ -519,24 +199,24 @@ class TTConfig:
|
|||||||
"TOP"
|
"TOP"
|
||||||
],
|
],
|
||||||
"stickers-suggestion-keywords-inline": False,
|
"stickers-suggestion-keywords-inline": False,
|
||||||
"support-account": "tt.me/support",
|
"support-account": "",
|
||||||
"support-button-enable": False,
|
"support-button-enable": False,
|
||||||
"t-ice-reconnect": 15,
|
"t-ice-reconnect": 15,
|
||||||
"t-incoming-call": 40,
|
"t-incoming-call": 40,
|
||||||
"t-start-connect": 20,
|
"t-start-connect": 20,
|
||||||
"tam-emoji-font-url": "https://st.okcdn.ru/static/messages/2022-08-25noto/TamNotoColorEmojiCompat.ttf",
|
"tam-emoji-font-url": "",
|
||||||
"tcp-candidates": False,
|
"tcp-candidates": False,
|
||||||
"tracer-crash-report-enabled": True,
|
"tracer-crash-report-enabled": False,
|
||||||
"tracer-crash-report-host": "https://api-hprof.odkl.ru",
|
"tracer-crash-report-host": "",
|
||||||
"tracer-crash-send-asap-enabled": True,
|
"tracer-crash-send-asap-enabled": False,
|
||||||
"tracer-crash-send-logs-enabled": True,
|
"tracer-crash-send-logs-enabled": False,
|
||||||
"tracer-crash-send-threads-dump-enabled": True,
|
"tracer-crash-send-threads-dump-enabled": False,
|
||||||
"tracer-disk-overflow-report-threshold": 3000000000,
|
"tracer-disk-overflow-report-threshold": 3000000000,
|
||||||
"tracer-disk-usage-probability": 500,
|
"tracer-disk-usage-probability": 500,
|
||||||
"tracer-enabled": True,
|
"tracer-enabled": False,
|
||||||
"tracer-host": "https://api-hprof.odkl.ru",
|
"tracer-host": "",
|
||||||
"tracer-hprof-probability": -1,
|
"tracer-hprof-probability": -1,
|
||||||
"tracer-sampled-conditions": "tag=app_start_ui_freeze_2k;probability=100000;startEvent=app_first_activity_created;interestingEvent=app_freeze;interestingDuration=2000",
|
"tracer-sampled-conditions": "",
|
||||||
"tracer-sampled-duration": 20000,
|
"tracer-sampled-duration": 20000,
|
||||||
"tracer-systrace-duration": 20000,
|
"tracer-systrace-duration": 20000,
|
||||||
"tracer-systrace-interesting-duration": 10000,
|
"tracer-systrace-interesting-duration": 10000,
|
||||||
@@ -569,4 +249,4 @@ class TTConfig:
|
|||||||
"iceServers": [],
|
"iceServers": [],
|
||||||
"has-phone": True,
|
"has-phone": True,
|
||||||
"promo-constructors": []
|
"promo-constructors": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,24 @@ import pydantic
|
|||||||
class UserAgentModel(pydantic.BaseModel):
|
class UserAgentModel(pydantic.BaseModel):
|
||||||
deviceType: str
|
deviceType: str
|
||||||
appVersion: str
|
appVersion: str
|
||||||
osVersion: str
|
osVersion: str = None
|
||||||
timezone: str
|
timezone: str = None
|
||||||
screen: str
|
screen: str = None
|
||||||
pushDeviceType: str = None
|
pushDeviceType: str = None
|
||||||
locale: str
|
locale: str = None
|
||||||
deviceName: str
|
deviceName: str
|
||||||
deviceLocale: str
|
deviceLocale: str = None
|
||||||
|
|
||||||
class HelloPayloadModel(pydantic.BaseModel):
|
class HelloPayloadModel(pydantic.BaseModel):
|
||||||
userAgent: UserAgentModel
|
userAgent: UserAgentModel
|
||||||
deviceId: str
|
deviceId: str = None
|
||||||
|
|
||||||
class RequestCodePayloadModel(pydantic.BaseModel):
|
class RequestCodePayloadModel(pydantic.BaseModel):
|
||||||
phone: str
|
phone: str
|
||||||
|
|
||||||
class VerifyCodePayloadModel(pydantic.BaseModel):
|
class VerifyCodePayloadModel(pydantic.BaseModel):
|
||||||
verifyCode: str
|
verifyCode: str
|
||||||
authTokenType: str
|
authTokenType: str = None
|
||||||
token: str
|
token: str
|
||||||
|
|
||||||
class FinalAuthPayloadModel(pydantic.BaseModel):
|
class FinalAuthPayloadModel(pydantic.BaseModel):
|
||||||
@@ -30,7 +30,7 @@ class FinalAuthPayloadModel(pydantic.BaseModel):
|
|||||||
token: str
|
token: str
|
||||||
|
|
||||||
class LoginPayloadModel(pydantic.BaseModel):
|
class LoginPayloadModel(pydantic.BaseModel):
|
||||||
interactive: bool
|
interactive: bool = None
|
||||||
token: str
|
token: str
|
||||||
|
|
||||||
class SearchUsersPayloadModel(pydantic.BaseModel):
|
class SearchUsersPayloadModel(pydantic.BaseModel):
|
||||||
@@ -41,4 +41,57 @@ class PingPayloadModel(pydantic.BaseModel):
|
|||||||
|
|
||||||
class ChatHistoryPayloadModel(pydantic.BaseModel):
|
class ChatHistoryPayloadModel(pydantic.BaseModel):
|
||||||
chatId: int
|
chatId: int
|
||||||
backward: int
|
backward: int
|
||||||
|
|
||||||
|
class UpdateProfilePayloadModel(pydantic.BaseModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SearchChatsPayloadModel(pydantic.BaseModel):
|
||||||
|
chatIds: list
|
||||||
|
|
||||||
|
class AssetsPayloadModel(pydantic.BaseModel):
|
||||||
|
sync: int
|
||||||
|
type: str = None
|
||||||
|
userId: int = None
|
||||||
|
|
||||||
|
class GetCallTokenPayloadModel(pydantic.BaseModel):
|
||||||
|
userId: int
|
||||||
|
value: str
|
||||||
|
|
||||||
|
class GetCallHistoryPayloadModel(pydantic.BaseModel):
|
||||||
|
forward: bool
|
||||||
|
count: int
|
||||||
|
|
||||||
|
class ChatSubscribePayloadModel(pydantic.BaseModel):
|
||||||
|
chatId: int
|
||||||
|
subscribe: bool
|
||||||
|
|
||||||
|
class ContactListPayloadModel(pydantic.BaseModel):
|
||||||
|
status: str
|
||||||
|
count: int = None
|
||||||
|
|
||||||
|
class ContactPresencePayloadModel(pydantic.BaseModel):
|
||||||
|
contactIds: list
|
||||||
|
|
||||||
|
class ContactUpdatePayloadModel(pydantic.BaseModel):
|
||||||
|
action: str
|
||||||
|
contactId: int
|
||||||
|
firstName: str
|
||||||
|
lastName: str = None
|
||||||
|
|
||||||
|
class TypingPayloadModel(pydantic.BaseModel):
|
||||||
|
chatId: int
|
||||||
|
type: str = None
|
||||||
|
|
||||||
|
class MessageModel(pydantic.BaseModel):
|
||||||
|
isLive: bool = None
|
||||||
|
detectShare: bool = None
|
||||||
|
elements: list = None
|
||||||
|
attaches: list = None
|
||||||
|
cid: int = None
|
||||||
|
text: str = None
|
||||||
|
|
||||||
|
class SendMessagePayloadModel(pydantic.BaseModel):
|
||||||
|
userId: int = None
|
||||||
|
chatId: int = None
|
||||||
|
message: MessageModel
|
||||||
@@ -2,9 +2,21 @@ from .main import MainProcessors
|
|||||||
from .auth import AuthProcessors
|
from .auth import AuthProcessors
|
||||||
from .search import SearchProcessors
|
from .search import SearchProcessors
|
||||||
from .history import HistoryProcessors
|
from .history import HistoryProcessors
|
||||||
|
from .assets import AssetsProcessors
|
||||||
|
from .calls import CallsProcessors
|
||||||
|
from .chats import ChatsProcessors
|
||||||
|
from .contacts import ContactsProcessors
|
||||||
|
from .messages import MessagesProcessors
|
||||||
|
from .sessions import SessionsProcessors
|
||||||
|
|
||||||
class Processors(MainProcessors,
|
class Processors(MainProcessors,
|
||||||
AuthProcessors,
|
AuthProcessors,
|
||||||
SearchProcessors,
|
SearchProcessors,
|
||||||
HistoryProcessors):
|
HistoryProcessors,
|
||||||
pass
|
AssetsProcessors,
|
||||||
|
CallsProcessors,
|
||||||
|
ChatsProcessors,
|
||||||
|
ContactsProcessors,
|
||||||
|
MessagesProcessors,
|
||||||
|
SessionsProcessors):
|
||||||
|
pass
|
||||||
|
|||||||
34
src/tamtam/processors/assets.py
Normal file
34
src/tamtam/processors/assets.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import pydantic
|
||||||
|
import time
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import AssetsPayloadModel
|
||||||
|
|
||||||
|
class AssetsProcessors(BaseProcessor):
|
||||||
|
async def assets_update(self, payload, seq, writer):
|
||||||
|
"""Обработчик запроса ассетов клиента на сервере"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
AssetsPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.ASSETS_UPDATE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: сейчас это заглушка, а попозже нужно сделать полноценную реализацию
|
||||||
|
|
||||||
|
# Данные пакета
|
||||||
|
payload = {
|
||||||
|
"sync": int(time.time() * 1000),
|
||||||
|
"stickerSetsUpdates": {},
|
||||||
|
"stickersUpdates": {},
|
||||||
|
"sections": [],
|
||||||
|
"stickersOrder": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.ASSETS_UPDATE, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, packet)
|
||||||
@@ -174,6 +174,9 @@ class AuthProcessors(BaseProcessor):
|
|||||||
if not deviceType:
|
if not deviceType:
|
||||||
deviceType = payload.get("deviceType")
|
deviceType = payload.get("deviceType")
|
||||||
|
|
||||||
|
if not deviceName:
|
||||||
|
deviceName = "Unknown device"
|
||||||
|
|
||||||
# Хешируем токен
|
# Хешируем токен
|
||||||
hashed_token = hashlib.sha256(token.encode()).hexdigest()
|
hashed_token = hashlib.sha256(token.encode()).hexdigest()
|
||||||
|
|
||||||
@@ -231,7 +234,9 @@ class AuthProcessors(BaseProcessor):
|
|||||||
|
|
||||||
# Собираем данные пакета
|
# Собираем данные пакета
|
||||||
payload = {
|
payload = {
|
||||||
"userToken": "0", # Пока как заглушка
|
# Я хз че сюда вставлять)
|
||||||
|
# ребята из одноклассников, может быть вы подскажете?
|
||||||
|
"userToken": str(account.get("id")),
|
||||||
"profile": self.tools.generate_profile_tt(
|
"profile": self.tools.generate_profile_tt(
|
||||||
id=account.get("id"),
|
id=account.get("id"),
|
||||||
phone=int(account.get("phone")),
|
phone=int(account.get("phone")),
|
||||||
@@ -338,16 +343,25 @@ class AuthProcessors(BaseProcessor):
|
|||||||
include_favourites=False
|
include_favourites=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Генерируем список контактов
|
||||||
|
contacts = await self.tools.collect_user_contacts(
|
||||||
|
user.get("id"), self.db_pool, self.config.avatar_base_url
|
||||||
|
)
|
||||||
|
|
||||||
|
# Собираем статусы контактов
|
||||||
|
contact_ids = [c.get("id") for c in contacts if c.get("id") is not None]
|
||||||
|
presence = await self.tools.collect_presence(contact_ids, self.clients, self.db_pool)
|
||||||
|
|
||||||
# Формируем данные пакета
|
# Формируем данные пакета
|
||||||
payload = {
|
payload = {
|
||||||
"profile": profile,
|
"profile": profile,
|
||||||
"chats": chats,
|
"chats": chats,
|
||||||
"chatMarker": 0,
|
"chatMarker": 0,
|
||||||
"messages": {},
|
"messages": {},
|
||||||
"contacts": [],
|
"contacts": contacts,
|
||||||
"presence": {},
|
"presence": presence,
|
||||||
"config": {
|
"config": {
|
||||||
"hash": "e5903aa8-0000000000000000-80000106-0000000000000001-00000001-0000000000000000-00000000-2-00000001-0000019c9559d057",
|
"hash": "0",
|
||||||
"server": self.server_config,
|
"server": self.server_config,
|
||||||
"user": updated_user_config,
|
"user": updated_user_config,
|
||||||
"chatFolders": {
|
"chatFolders": {
|
||||||
@@ -371,6 +385,10 @@ class AuthProcessors(BaseProcessor):
|
|||||||
"time": int(time.time() * 1000)
|
"time": int(time.time() * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# print(
|
||||||
|
# json.dumps(payload, indent=4)
|
||||||
|
# )
|
||||||
|
|
||||||
# Собираем пакет
|
# Собираем пакет
|
||||||
packet = self.proto.pack_packet(
|
packet = self.proto.pack_packet(
|
||||||
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGIN, payload=payload
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGIN, payload=payload
|
||||||
@@ -378,4 +396,21 @@ class AuthProcessors(BaseProcessor):
|
|||||||
|
|
||||||
# Отправляем
|
# Отправляем
|
||||||
await self._send(writer, packet)
|
await self._send(writer, packet)
|
||||||
return int(user.get("phone")), int(user.get("id")), hashed_token
|
return int(user.get("phone")), int(user.get("id")), hashed_token
|
||||||
|
|
||||||
|
async def logout(self, seq, writer, hashedToken):
|
||||||
|
"""Обработчик завершения сессии"""
|
||||||
|
# Удаляем токен из бд
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"DELETE FROM tokens WHERE token_hash = %s", (hashedToken,)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем пакет
|
||||||
|
response = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGOUT, payload=None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, response)
|
||||||
20
src/tamtam/processors/chats.py
Normal file
20
src/tamtam/processors/chats.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import pydantic
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import ChatSubscribePayloadModel
|
||||||
|
|
||||||
|
class ChatsProcessors(BaseProcessor):
|
||||||
|
async def chat_subscribe(self, payload, seq, writer):
|
||||||
|
# Валидируем входные данные
|
||||||
|
try:
|
||||||
|
ChatSubscribePayloadModel.model_validate(payload)
|
||||||
|
except Exception as e:
|
||||||
|
await self._send_error(seq, self.opcodes.CHAT_SUBSCRIBE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Созадаем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_SUBSCRIBE, payload=None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправялем
|
||||||
|
await self._send(writer, packet)
|
||||||
188
src/tamtam/processors/contacts.py
Normal file
188
src/tamtam/processors/contacts.py
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import pydantic
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import ContactListPayloadModel, ContactPresencePayloadModel, ContactUpdatePayloadModel
|
||||||
|
|
||||||
|
class ContactsProcessors(BaseProcessor):
|
||||||
|
async def contact_list(self, payload, seq, writer, userId):
|
||||||
|
"""Обработчик получения контактов"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
ContactListPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.CONTACT_LIST, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
status = payload.get("status")
|
||||||
|
count = payload.get("count")
|
||||||
|
|
||||||
|
# Итоговый контакт-лист
|
||||||
|
contact_list = []
|
||||||
|
|
||||||
|
if status == "BLOCKED":
|
||||||
|
# Собираем контакты, которые в черном списке
|
||||||
|
blocked = []
|
||||||
|
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
if count:
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT * FROM contacts WHERE owner_id = %s AND is_blocked = TRUE LIMIT %s",
|
||||||
|
(userId, count),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT * FROM contacts WHERE owner_id = %s AND is_blocked = TRUE",
|
||||||
|
(userId,),
|
||||||
|
)
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
blocked.append(
|
||||||
|
{
|
||||||
|
"id": int(row.get("contact_id")),
|
||||||
|
"firstname": row.get("custom_firstname"),
|
||||||
|
"lastname": row.get("custom_lastname"),
|
||||||
|
"blocked": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Генерируем контакт-лист
|
||||||
|
contact_list = await self.tools.generate_contacts(
|
||||||
|
blocked, self.db_pool, avatar_base_url=self.config.avatar_base_url
|
||||||
|
)
|
||||||
|
|
||||||
|
# Собираем данные пакета
|
||||||
|
response_payload = {
|
||||||
|
"contacts": contact_list
|
||||||
|
}
|
||||||
|
|
||||||
|
# Создаем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
seq=seq, opcode=self.opcodes.CONTACT_LIST, payload=response_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем пакет
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
async def contact_update(self, payload, seq, writer, userId):
|
||||||
|
"""
|
||||||
|
Обработчик опкода какого-то там
|
||||||
|
(их хуй запомнишь, даже в мриме команды помню, бля)
|
||||||
|
|
||||||
|
Отвечает за добавку, удаление, блокировку и разблокировку контакта
|
||||||
|
"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
ContactUpdatePayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.CONTACT_UPDATE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
action = payload.get("action")
|
||||||
|
contactId = payload.get("contactId")
|
||||||
|
firstName = payload.get("firstName")
|
||||||
|
lastName = payload.get("lastName", "")
|
||||||
|
|
||||||
|
if action == "ADD":
|
||||||
|
# Проверяем, существует ли пользователь с таким ID
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute("SELECT * FROM users WHERE id = %s", (contactId,))
|
||||||
|
user = await cursor.fetchone()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
await self._send_error(seq, self.opcodes.CONTACT_UPDATE, self.error_types.USER_NOT_FOUND, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Проверяем, не добавлен ли уже контакт
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT * FROM contacts WHERE owner_id = %s AND contact_id = %s",
|
||||||
|
(userId, contactId)
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
# Если контакта не существует, то можем продолжать,
|
||||||
|
if not row:
|
||||||
|
# Добавляем контакт
|
||||||
|
await cursor.execute(
|
||||||
|
"INSERT INTO contacts (owner_id, contact_id, custom_firstname, custom_lastname, is_blocked) VALUES (%s, %s, %s, %s, FALSE)",
|
||||||
|
(userId, contactId, firstName, lastName)
|
||||||
|
)
|
||||||
|
# а если уже существует, отправляем ошибку
|
||||||
|
else:
|
||||||
|
await self._send_error(seq, self.opcodes.CONTACT_UPDATE, self.error_types.CONTACT_ALREADY_EXISTS, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Генерируем профиль
|
||||||
|
photoId = None if not user.get("avatar_id") else int(user.get("avatar_id"))
|
||||||
|
avatar_url = None if not photoId else self.config.avatar_base_url + str(photoId)
|
||||||
|
|
||||||
|
contact = self.tools.generate_profile(
|
||||||
|
id=user.get("id"),
|
||||||
|
phone=int(user.get("phone")),
|
||||||
|
avatarUrl=avatar_url,
|
||||||
|
photoId=photoId,
|
||||||
|
updateTime=int(user.get("updatetime")),
|
||||||
|
firstName=user.get("firstname"),
|
||||||
|
lastName=user.get("lastname"),
|
||||||
|
options=json.loads(user.get("options")),
|
||||||
|
accountStatus=int(user.get("accountstatus")),
|
||||||
|
includeProfileOptions=False,
|
||||||
|
custom_firstname=firstName,
|
||||||
|
custom_lastname=lastName,
|
||||||
|
)
|
||||||
|
|
||||||
|
response_payload = {
|
||||||
|
"contact": contact
|
||||||
|
}
|
||||||
|
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_UPDATE, payload=response_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
elif action == "REMOVE":
|
||||||
|
# Удаляем контакт
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"DELETE FROM contacts WHERE owner_id = %s AND contact_id = %s",
|
||||||
|
(userId, contactId)
|
||||||
|
)
|
||||||
|
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_UPDATE, payload=None
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
async def contact_presence(self, payload, seq, writer):
|
||||||
|
"""Обработчик получения статуса контактов"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
ContactPresencePayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.CONTACT_PRESENCE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
contact_ids = payload.get("contactIds", [])
|
||||||
|
now_ms = int(time.time() * 1000)
|
||||||
|
|
||||||
|
presence = await self.tools.collect_presence(contact_ids, self.clients, self.db_pool)
|
||||||
|
|
||||||
|
response_payload = {
|
||||||
|
"presence": presence,
|
||||||
|
"time": now_ms
|
||||||
|
}
|
||||||
|
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_PRESENCE, payload=response_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send(writer, packet)
|
||||||
133
src/tamtam/processors/folders.py
Normal file
133
src/tamtam/processors/folders.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import pydantic
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import SyncFoldersPayloadModel, CreateFolderPayloadModel
|
||||||
|
|
||||||
|
class FoldersProcessors(BaseProcessor):
|
||||||
|
async def folders_get(self, payload, seq, writer, senderPhone):
|
||||||
|
"""Синхронизация папок с сервером"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
SyncFoldersPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.FOLDERS_GET, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ищем папки в бд
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT id, title, filters, `include`, options, update_time, source_id "
|
||||||
|
"FROM user_folders WHERE phone = %s ORDER BY sort_order",
|
||||||
|
(int(senderPhone),)
|
||||||
|
)
|
||||||
|
result_folders = await cursor.fetchall()
|
||||||
|
|
||||||
|
folders = [
|
||||||
|
{
|
||||||
|
"id": folder["id"],
|
||||||
|
"title": folder["title"],
|
||||||
|
"filters": json.loads(folder["filters"]),
|
||||||
|
"include": json.loads(folder["include"]),
|
||||||
|
"updateTime": folder["update_time"],
|
||||||
|
"options": json.loads(folder["options"]),
|
||||||
|
"sourceId": folder["source_id"]
|
||||||
|
}
|
||||||
|
for folder in result_folders
|
||||||
|
]
|
||||||
|
|
||||||
|
# Создаем данные пакета
|
||||||
|
payload = {
|
||||||
|
"folderSync": int(time.time() * 1000),
|
||||||
|
"folders": folders,
|
||||||
|
"foldersOrder": [folder["id"] for folder in result_folders],
|
||||||
|
"allFilterExcludeFolders": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.FOLDERS_GET, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
async def folders_update(self, payload, seq, writer, senderPhone):
|
||||||
|
"""Создание папки"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
CreateFolderPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.FOLDERS_UPDATE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
update_time = int(time.time() * 1000)
|
||||||
|
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT COALESCE(MAX(sort_order), -1) as max_order FROM user_folders WHERE phone = %s",
|
||||||
|
(int(senderPhone),)
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
next_order = row["max_order"] + 1
|
||||||
|
|
||||||
|
# Создаем новую папку
|
||||||
|
await cursor.execute(
|
||||||
|
"INSERT INTO user_folders (id, phone, title, filters, `include`, options, source_id, update_time, sort_order) "
|
||||||
|
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
||||||
|
(
|
||||||
|
payload.get("id"),
|
||||||
|
int(senderPhone),
|
||||||
|
payload.get("title"),
|
||||||
|
json.dumps(payload.get("filters")),
|
||||||
|
json.dumps(payload.get("include", [])),
|
||||||
|
json.dumps([]),
|
||||||
|
1,
|
||||||
|
update_time,
|
||||||
|
next_order,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await conn.commit()
|
||||||
|
|
||||||
|
# Получаем обновленный порядок папок
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT id FROM user_folders WHERE phone = %s ORDER BY sort_order",
|
||||||
|
(int(senderPhone),)
|
||||||
|
)
|
||||||
|
all_folders = await cursor.fetchall()
|
||||||
|
|
||||||
|
folders_order = [f["id"] for f in all_folders]
|
||||||
|
|
||||||
|
# Формируем данные пакета
|
||||||
|
response_payload = {
|
||||||
|
"folder": {
|
||||||
|
"id": payload.get("id"),
|
||||||
|
"title": payload.get("title"),
|
||||||
|
"include": payload.get("include"),
|
||||||
|
"filters": payload.get("filters"),
|
||||||
|
"updateTime": update_time,
|
||||||
|
"options": [],
|
||||||
|
"sourceId": 1,
|
||||||
|
},
|
||||||
|
"folderSync": update_time,
|
||||||
|
"foldersOrder": folders_order,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Формируем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.FOLDERS_UPDATE, payload=response_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
# Разработчики протокола, объяснитесь, что за хеш !!! а еще подарите нам способ его формирования
|
||||||
|
notify_about_hash = self.proto.pack_packet(
|
||||||
|
cmd=0, seq=1, opcode=self.opcodes.NOTIF_CONFIG,
|
||||||
|
payload={"config": {"hash": "0"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send(writer, notify_about_hash)
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import json
|
||||||
import pydantic
|
import pydantic
|
||||||
from classes.baseprocessor import BaseProcessor
|
from classes.baseprocessor import BaseProcessor
|
||||||
from tamtam.models import HelloPayloadModel, PingPayloadModel
|
from tamtam.models import HelloPayloadModel, PingPayloadModel
|
||||||
|
from tamtam.models import UpdateProfilePayloadModel
|
||||||
|
|
||||||
class MainProcessors(BaseProcessor):
|
class MainProcessors(BaseProcessor):
|
||||||
async def session_init(self, payload, seq, writer):
|
async def session_init(self, payload, seq, writer):
|
||||||
@@ -35,6 +37,106 @@ class MainProcessors(BaseProcessor):
|
|||||||
# Отправляем
|
# Отправляем
|
||||||
await self._send(writer, packet)
|
await self._send(writer, packet)
|
||||||
return device_type, device_name
|
return device_type, device_name
|
||||||
|
|
||||||
|
async def profile(self, payload, seq, writer, userId):
|
||||||
|
"""Обработчик получения/обновления профиля"""
|
||||||
|
# Валидируем входные данные
|
||||||
|
try:
|
||||||
|
UpdateProfilePayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.PROFILE, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ищем пользователя в бд
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute("SELECT * FROM users WHERE id = %s", (userId,))
|
||||||
|
user = await cursor.fetchone()
|
||||||
|
|
||||||
|
# Если пользователь не найден
|
||||||
|
if not user:
|
||||||
|
await self._send_error(seq, self.opcodes.PROFILE, self.error_types.USER_NOT_FOUND, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Аватарка с биографией
|
||||||
|
photo_id = int(user["avatar_id"]) if user.get("avatar_id") else None
|
||||||
|
avatar_url = f"{self.config.avatar_base_url}{photo_id}" if photo_id else None
|
||||||
|
description = user.get("description")
|
||||||
|
|
||||||
|
# Генерируем профиль
|
||||||
|
profile = self.tools.generate_profile_tt(
|
||||||
|
id=user.get("id"),
|
||||||
|
phone=int(user.get("phone")),
|
||||||
|
avatarUrl=avatar_url,
|
||||||
|
photoId=photo_id,
|
||||||
|
updateTime=int(user.get("updatetime")),
|
||||||
|
firstName=user.get("firstname"),
|
||||||
|
lastName=user.get("lastname"),
|
||||||
|
options=json.loads(user.get("options")),
|
||||||
|
description=description,
|
||||||
|
username=user.get("username")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем данные пакета
|
||||||
|
payload = {
|
||||||
|
"profile": profile
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
response = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.PROFILE, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, response)
|
||||||
|
|
||||||
|
async def update_config(self, payload, seq, writer, userPhone, hashedToken=None):
|
||||||
|
"""Обработчик обновления настроек и пуш-токена"""
|
||||||
|
result_payload = None
|
||||||
|
|
||||||
|
if payload.get("pushToken"):
|
||||||
|
push_token = payload.get("pushToken")
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"UPDATE tokens SET push_token = %s WHERE phone = %s AND token_hash = %s",
|
||||||
|
(push_token, str(userPhone), hashedToken)
|
||||||
|
)
|
||||||
|
elif payload.get("settings") and payload.get("settings").get("user"):
|
||||||
|
new_settings = payload.get("settings").get("user")
|
||||||
|
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(
|
||||||
|
"SELECT user_config FROM user_data WHERE phone = %s", (userPhone,)
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if row:
|
||||||
|
current_config = json.loads(row.get("user_config"))
|
||||||
|
|
||||||
|
for key, value in new_settings.items():
|
||||||
|
if key in current_config:
|
||||||
|
current_config[key] = value
|
||||||
|
|
||||||
|
await cursor.execute(
|
||||||
|
"UPDATE user_data SET user_config = %s WHERE phone = %s",
|
||||||
|
(json.dumps(current_config), userPhone)
|
||||||
|
)
|
||||||
|
|
||||||
|
result_payload = {
|
||||||
|
"user": current_config,
|
||||||
|
"hash": "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
response = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONFIG, payload=result_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, response)
|
||||||
|
|
||||||
async def ping(self, payload, seq, writer):
|
async def ping(self, payload, seq, writer):
|
||||||
"""Обработчик пинга"""
|
"""Обработчик пинга"""
|
||||||
|
|||||||
160
src/tamtam/processors/messages.py
Normal file
160
src/tamtam/processors/messages.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import pydantic
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import (
|
||||||
|
TypingPayloadModel,
|
||||||
|
SendMessagePayloadModel
|
||||||
|
)
|
||||||
|
|
||||||
|
class MessagesProcessors(BaseProcessor):
|
||||||
|
async def msg_typing(self, payload, seq, writer, senderId):
|
||||||
|
"""Обработчик события печатания"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
TypingPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Извлекаем данные из пакета
|
||||||
|
chatId = payload.get("chatId")
|
||||||
|
type = payload.get("type") or "TYPING"
|
||||||
|
|
||||||
|
# Ищем чат в базе данных
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,))
|
||||||
|
chat = await cursor.fetchone()
|
||||||
|
|
||||||
|
# Если чат не найден, отправляем ошибку
|
||||||
|
if not chat:
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.CHAT_NOT_FOUND, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Участники чата
|
||||||
|
participants = await self.tools.get_chat_participants(chatId, self.db_pool)
|
||||||
|
|
||||||
|
# Проверяем, является ли отправитель участником чата
|
||||||
|
if int(senderId) not in participants:
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.CHAT_NOT_ACCESS, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Рассылаем событие участникам чата
|
||||||
|
for participant in participants:
|
||||||
|
if participant != senderId:
|
||||||
|
# Если участник не является отправителем, отправляем
|
||||||
|
await self.event(
|
||||||
|
participant,
|
||||||
|
{
|
||||||
|
"eventType": "typing",
|
||||||
|
"chatId": chatId,
|
||||||
|
"type": type,
|
||||||
|
"userId": senderId,
|
||||||
|
"writer": writer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
seq=seq, opcode=self.opcodes.MSG_TYPING
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем пакет
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
||||||
|
async def msg_send(self, payload, seq, writer, senderId, db_pool):
|
||||||
|
"""Функция отправки сообщения"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
SendMessagePayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Извлекаем данные из пакета
|
||||||
|
userId = payload.get("userId")
|
||||||
|
chatId = payload.get("chatId")
|
||||||
|
message = payload.get("message")
|
||||||
|
|
||||||
|
elements = message.get("elements") or []
|
||||||
|
attaches = message.get("attaches") or []
|
||||||
|
cid = message.get("cid") or 0
|
||||||
|
text = message.get("text") or ""
|
||||||
|
|
||||||
|
# Вычисляем ID чата по ID пользователя и ID отправителя,
|
||||||
|
# в случае отсутствия ID чата
|
||||||
|
if chatId is None:
|
||||||
|
chatId = userId ^ senderId
|
||||||
|
|
||||||
|
async with db_pool.acquire() as db_connection:
|
||||||
|
async with db_connection.cursor() as cursor:
|
||||||
|
await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,))
|
||||||
|
chat = await cursor.fetchone()
|
||||||
|
|
||||||
|
# Если нет такого чата - выбрасываем ошибку
|
||||||
|
if not chat:
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.CHAT_NOT_FOUND, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Список участников
|
||||||
|
participants = await self.tools.get_chat_participants(chatId, db_pool)
|
||||||
|
|
||||||
|
# Проверяем, является ли отправитель участником чата
|
||||||
|
if int(senderId) not in participants:
|
||||||
|
await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.CHAT_NOT_ACCESS, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Добавляем сообщение в историю
|
||||||
|
messageId, lastMessageId, messageTime = await self.tools.insert_message(
|
||||||
|
chatId=chatId,
|
||||||
|
senderId=senderId,
|
||||||
|
text=text,
|
||||||
|
attaches=attaches,
|
||||||
|
elements=elements,
|
||||||
|
cid=cid,
|
||||||
|
type="USER",
|
||||||
|
db_pool=self.db_pool
|
||||||
|
)
|
||||||
|
|
||||||
|
# Готовое тело сообщения
|
||||||
|
bodyMessage = {
|
||||||
|
"id": messageId,
|
||||||
|
"time": messageTime,
|
||||||
|
"type": "USER",
|
||||||
|
"sender": senderId,
|
||||||
|
"cid": cid,
|
||||||
|
"text": text,
|
||||||
|
"attaches": attaches,
|
||||||
|
"elements": elements
|
||||||
|
}
|
||||||
|
|
||||||
|
# Отправляем событие всем участникам чата
|
||||||
|
for participant in participants:
|
||||||
|
await self.event(
|
||||||
|
participant,
|
||||||
|
{
|
||||||
|
"eventType": "new_msg",
|
||||||
|
"chatId": 0 if chatId == senderId else chatId,
|
||||||
|
"message": bodyMessage,
|
||||||
|
"prevMessageId": lastMessageId,
|
||||||
|
"time": messageTime,
|
||||||
|
"writer": writer
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Данные пакета
|
||||||
|
payload = {
|
||||||
|
"chatId": 0 if chatId == senderId else chatId,
|
||||||
|
"message": bodyMessage,
|
||||||
|
"unread": 0,
|
||||||
|
"mark": messageTime
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.MSG_SEND, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, packet)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import json, pydantic
|
import json, pydantic
|
||||||
from classes.baseprocessor import BaseProcessor
|
from classes.baseprocessor import BaseProcessor
|
||||||
from tamtam.models import SearchUsersPayloadModel
|
from tamtam.models import SearchUsersPayloadModel
|
||||||
|
from tamtam.models import SearchChatsPayloadModel
|
||||||
|
|
||||||
class SearchProcessors(BaseProcessor):
|
class SearchProcessors(BaseProcessor):
|
||||||
async def contact_info(self, payload, seq, writer):
|
async def contact_info(self, payload, seq, writer):
|
||||||
@@ -59,5 +60,61 @@ class SearchProcessors(BaseProcessor):
|
|||||||
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_INFO, payload=payload
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_INFO, payload=payload
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, response)
|
||||||
|
|
||||||
|
async def chat_info(self, payload, seq, writer, senderId):
|
||||||
|
"""Поиск чатов по ID"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
SearchChatsPayloadModel.model_validate(payload)
|
||||||
|
except pydantic.ValidationError as error:
|
||||||
|
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
||||||
|
await self._send_error(seq, self.opcodes.CHAT_INFO, self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Итоговый список чатов
|
||||||
|
chats = []
|
||||||
|
|
||||||
|
# ID чатов, которые нам предстоит найти
|
||||||
|
chatIds = payload.get("chatIds")
|
||||||
|
|
||||||
|
# Ищем чаты в бд
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
for chatId in chatIds:
|
||||||
|
await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,))
|
||||||
|
chat = await cursor.fetchone()
|
||||||
|
|
||||||
|
if chat:
|
||||||
|
# Проверяем, является ли пользователь участником чата
|
||||||
|
participants = await self.tools.get_chat_participants(chatId, self.db_pool)
|
||||||
|
if int(senderId) not in participants:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Получаем последнее сообщение из чата
|
||||||
|
message, messageTime = await self.tools.get_last_message(
|
||||||
|
chatId, self.db_pool, protocol_type=self.type
|
||||||
|
)
|
||||||
|
|
||||||
|
# Добавляем чат в список
|
||||||
|
chats.append(
|
||||||
|
self.tools.generate_chat(
|
||||||
|
chatId, chat.get("owner"),
|
||||||
|
chat.get("type"), participants,
|
||||||
|
message, messageTime
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем данные пакета
|
||||||
|
payload = {
|
||||||
|
"chats": chats
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
response = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_INFO, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
# Отправляем
|
# Отправляем
|
||||||
await self._send(writer, response)
|
await self._send(writer, response)
|
||||||
38
src/tamtam/processors/sessions.py
Normal file
38
src/tamtam/processors/sessions.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
|
||||||
|
class SessionsProcessors(BaseProcessor):
|
||||||
|
async def sessions_info(self, payload, seq, writer, senderPhone, hashedToken):
|
||||||
|
"""Получение активных сессий на аккаунте"""
|
||||||
|
# Готовый список сессий
|
||||||
|
sessions = []
|
||||||
|
|
||||||
|
# Ищем сессии в бд
|
||||||
|
async with self.db_pool.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute("SELECT * FROM tokens WHERE phone = %s", (str(senderPhone),))
|
||||||
|
user_sessions = await cursor.fetchall()
|
||||||
|
|
||||||
|
# Собираем сессии в список
|
||||||
|
for session in user_sessions:
|
||||||
|
sessions.append(
|
||||||
|
{
|
||||||
|
"time": int(session.get("time")),
|
||||||
|
"client": f"TamTam {session.get('device_type')}",
|
||||||
|
"info": session.get("device_name"),
|
||||||
|
"location": session.get("location"),
|
||||||
|
"current": True if session.get("token_hash") == hashedToken else False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем данные пакета
|
||||||
|
payload = {
|
||||||
|
"sessions": sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
# Создаем пакет
|
||||||
|
response = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSIONS_INFO, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, response)
|
||||||
@@ -24,7 +24,7 @@ class TamTamMobile:
|
|||||||
self.auth_required = Tools().auth_required
|
self.auth_required = Tools().auth_required
|
||||||
|
|
||||||
# rate limiter
|
# rate limiter
|
||||||
self.auth_rate_limiter = RateLimiter(max_attempts=5, window_seconds=60)
|
self.auth_rate_limiter = RateLimiter(max_attempts=15, window_seconds=60)
|
||||||
|
|
||||||
self.read_timeout = 300 # Таймаут чтения из сокета (секунды)
|
self.read_timeout = 300 # Таймаут чтения из сокета (секунды)
|
||||||
self.max_read_size = 65536 # Максимальный размер данных из сокета
|
self.max_read_size = 65536 # Максимальный размер данных из сокета
|
||||||
@@ -105,18 +105,91 @@ class TamTamMobile:
|
|||||||
|
|
||||||
if userPhone:
|
if userPhone:
|
||||||
await self._finish_auth(writer, address, userPhone, userId)
|
await self._finish_auth(writer, address, userPhone, userId)
|
||||||
|
case self.opcodes.LOGOUT:
|
||||||
|
await self.processors.logout(
|
||||||
|
seq, writer, hashedToken=hashedToken
|
||||||
|
)
|
||||||
|
break
|
||||||
case self.opcodes.CONTACT_INFO:
|
case self.opcodes.CONTACT_INFO:
|
||||||
await self.auth_required(
|
await self.auth_required(
|
||||||
userPhone, self.processors.contact_info, payload, seq, writer
|
userPhone, self.processors.contact_info, payload, seq, writer
|
||||||
)
|
)
|
||||||
|
case self.opcodes.CHAT_HISTORY:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.chat_history, payload, seq, writer, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.ASSETS_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.assets_update, payload, seq, writer
|
||||||
|
)
|
||||||
|
case self.opcodes.VIDEO_CHAT_HISTORY:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.video_chat_history, payload, seq, writer
|
||||||
|
)
|
||||||
|
case self.opcodes.MSG_SEND:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.msg_send, payload, seq, writer, userId, self.db_pool
|
||||||
|
)
|
||||||
|
case self.opcodes.MSG_TYPING:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.msg_typing, payload, seq, writer, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.FOLDERS_GET:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.folders_get, payload, seq, writer, userPhone
|
||||||
|
)
|
||||||
|
case self.opcodes.FOLDERS_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.folders_update, payload, seq, writer, userPhone
|
||||||
|
)
|
||||||
|
case self.opcodes.SESSIONS_INFO:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.sessions_info, payload, seq, writer, userPhone, hashedToken
|
||||||
|
)
|
||||||
|
case self.opcodes.CHAT_INFO:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.chat_info, payload, seq, writer, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.OK_TOKEN:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.ok_token, payload, seq, writer
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_LIST:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_list, payload, seq, writer, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.PROFILE:
|
||||||
|
await self.processors.profile(
|
||||||
|
payload, seq, writer, userId=userId
|
||||||
|
)
|
||||||
|
case self.opcodes.CHAT_SUBSCRIBE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.chat_subscribe, payload, seq, writer
|
||||||
|
)
|
||||||
|
case self.opcodes.CONFIG:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.update_config, payload, seq, writer, userPhone, hashedToken
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_update, payload, seq, writer, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_PRESENCE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_presence, payload, seq, writer
|
||||||
|
)
|
||||||
case _:
|
case _:
|
||||||
self.logger.warning(f"Неизвестный опкод {opcode}")
|
self.logger.warning(f"Неизвестный опкод {opcode}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Произошла ошибка при работе с клиентом {address[0]}:{address[1]}: {e}")
|
self.logger.error(f"Произошла ошибка при работе с клиентом {address[0]}:{address[1]}: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# Удаляем клиента из словаря при отключении
|
||||||
|
if userId:
|
||||||
|
await self._end_session(userId, address[0], address[1])
|
||||||
|
|
||||||
writer.close()
|
writer.close()
|
||||||
self.logger.info(f"Прекратил работать работать с клиентом {address[0]}:{address[1]}")
|
self.logger.info(f"Прекратил работать с клиентом {address[0]}:{address[1]}")
|
||||||
|
|
||||||
async def _finish_auth(self, writer, addr, phone, id):
|
async def _finish_auth(self, writer, addr, phone, id):
|
||||||
"""Завершение открытия сессии"""
|
"""Завершение открытия сессии"""
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ class TamTamWS:
|
|||||||
|
|
||||||
if userPhone:
|
if userPhone:
|
||||||
await self._finish_auth(websocket, address, userPhone, userId)
|
await self._finish_auth(websocket, address, userPhone, userId)
|
||||||
|
case self.opcodes.LOGOUT:
|
||||||
|
await self.processors.logout(
|
||||||
|
seq, websocket, hashedToken=hashedToken
|
||||||
|
)
|
||||||
|
break
|
||||||
case self.opcodes.CONTACT_INFO:
|
case self.opcodes.CONTACT_INFO:
|
||||||
await self.auth_required(
|
await self.auth_required(
|
||||||
userPhone, self.processors.contact_info, payload, seq, websocket
|
userPhone, self.processors.contact_info, payload, seq, websocket
|
||||||
@@ -99,6 +104,66 @@ class TamTamWS:
|
|||||||
await self.auth_required(
|
await self.auth_required(
|
||||||
userPhone, self.processors.chat_history, payload, seq, websocket, userId
|
userPhone, self.processors.chat_history, payload, seq, websocket, userId
|
||||||
)
|
)
|
||||||
|
case self.opcodes.ASSETS_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.assets_update, payload, seq, websocket
|
||||||
|
)
|
||||||
|
case self.opcodes.VIDEO_CHAT_HISTORY:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.video_chat_history, payload, seq, websocket
|
||||||
|
)
|
||||||
|
case self.opcodes.MSG_SEND:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.msg_send, payload, seq, websocket, userId, self.db_pool
|
||||||
|
)
|
||||||
|
case self.opcodes.MSG_TYPING:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.msg_typing, payload, seq, websocket, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.FOLDERS_GET:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.folders_get, payload, seq, websocket, userPhone
|
||||||
|
)
|
||||||
|
case self.opcodes.FOLDERS_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.folders_update, payload, seq, websocket, userPhone
|
||||||
|
)
|
||||||
|
case self.opcodes.SESSIONS_INFO:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.sessions_info, payload, seq, websocket, userPhone, hashedToken
|
||||||
|
)
|
||||||
|
case self.opcodes.CHAT_INFO:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.chat_info, payload, seq, websocket, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.OK_TOKEN:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.ok_token, payload, seq, websocket
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_LIST:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_list, payload, seq, websocket, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.PROFILE:
|
||||||
|
await self.processors.profile(
|
||||||
|
payload, seq, websocket, userId=userId
|
||||||
|
)
|
||||||
|
case self.opcodes.CHAT_SUBSCRIBE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.chat_subscribe, payload, seq, websocket
|
||||||
|
)
|
||||||
|
case self.opcodes.CONFIG:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.update_config, payload, seq, websocket, userPhone, hashedToken
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_UPDATE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_update, payload, seq, websocket, userId
|
||||||
|
)
|
||||||
|
case self.opcodes.CONTACT_PRESENCE:
|
||||||
|
await self.auth_required(
|
||||||
|
userPhone, self.processors.contact_presence, payload, seq, websocket
|
||||||
|
)
|
||||||
case _:
|
case _:
|
||||||
self.logger.warning(f"Неизвестный опкод {opcode}")
|
self.logger.warning(f"Неизвестный опкод {opcode}")
|
||||||
except websockets.exceptions.ConnectionClosed:
|
except websockets.exceptions.ConnectionClosed:
|
||||||
|
|||||||
Reference in New Issue
Block a user