Compare commits
376 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a3723a7ee | |||
| 48ec267f7f | |||
| 852f3b2359 | |||
| 8584c8daff | |||
| 1d9af5bafb | |||
| 1c9278d050 | |||
| 2ee8fe532d | |||
| decef2ca9e | |||
| f1bf1275b8 | |||
| b3dc9d1de1 | |||
| 43e2b4d85f | |||
| d2c6c5b14a | |||
| 8e895bb78d | |||
| 89ae7cfca2 | |||
| d0ca602f3f | |||
| 8ad26c0426 | |||
| e38fbf814b | |||
| 67c946f24a | |||
| 2647cefdcc | |||
| c1e5d2a781 | |||
| 9cf11ff45d | |||
| d2272ec75e | |||
| f8b0d788a2 | |||
| 3e0ee584aa | |||
| 698e4a0af4 | |||
| 5b7926c590 | |||
| 1f6d0b143c | |||
| 8558af8189 | |||
| 28ceb0bed2 | |||
| 41523b03d1 | |||
| 4a46207fa5 | |||
| dfb9ac91e9 | |||
| 6229c0020c | |||
| 8db1bf9052 | |||
| 2740c5db23 | |||
| 512e6de39b | |||
| e3c328c7e3 | |||
| 9eb5ea599e | |||
| 929032d598 | |||
| 09fb42291d | |||
| e6665a8305 | |||
| a785d83791 | |||
| 08a64f0dc0 | |||
| 0babad837d | |||
| fb1f70f5de | |||
| d4dd1b7ba0 | |||
| 82c921d739 | |||
| 180a570f99 | |||
| 9960a7a7e4 | |||
| 2164d08c3c | |||
| 4b2d0d1a67 | |||
| d0341d191b | |||
| 10237a2d90 | |||
| 59390ddaa1 | |||
| 312c94f386 | |||
| d697677fab | |||
| bf484b5518 | |||
| 74b530259d | |||
| 4052c3d101 | |||
| 86262333a4 | |||
| 3419b49381 | |||
| ec5725d8de | |||
| 9df98352a8 | |||
| c0c5ad21b6 | |||
| 4a23da6ce1 | |||
| 6d1e5243d8 | |||
| c52a31384e | |||
| 36447eb1e3 | |||
| 811dfee42c | |||
| b075bb9f4e | |||
| 00919f3232 | |||
| 6dcd52deb5 | |||
| edc70e9acb | |||
| 4ad12b6862 | |||
| d63f3b6da1 | |||
| 84c70a0905 | |||
| 8f65a7ac19 | |||
| c78262be91 | |||
| cb51238bfc | |||
| 87f873c8b6 | |||
| a37528b377 | |||
| 51a58182ee | |||
| cc4b768f54 | |||
| b016be6eb5 | |||
| 881d73d4ab | |||
| 8859e352f9 | |||
| 5b0fc5b97b | |||
| 76cc59acab | |||
| e792088ceb | |||
| 1dc70d5a8d | |||
| e876a0f6bd | |||
| 50450de3e4 | |||
| c8abc84c3c | |||
| 1f3996032c | |||
| b868cfdca9 | |||
| d51944e6a5 | |||
| 4419ba55e8 | |||
| 9e8ae54821 | |||
| 4ed266780a | |||
| 46886b4dcc | |||
| 1e2531f0b1 | |||
| 0e43fd4d00 | |||
| 10448bcc3d | |||
| 4f1c2788b8 | |||
| dbaa7b5e67 | |||
| 6211b7733d | |||
| 0d6ea0845e | |||
| b8e10fb34b | |||
| b9c6e595d7 | |||
| 7d701d3e9c | |||
| 92d9c3c92b | |||
| 7007977891 | |||
| 9f5d24bbc9 | |||
| 2834a83eec | |||
| eb82473395 | |||
| 4a33d3227e | |||
| 235456e18e | |||
| 1f9e532fbc | |||
| 8bc2d3184a | |||
| 9ef54d1218 | |||
| 7e48cb97b0 | |||
| a226f0b58c | |||
| 65fd47a082 | |||
| 600b7d1d54 | |||
| b8f4d4877e | |||
| 6a114e1275 | |||
| 5d3d8640e3 | |||
| f09d2d22eb | |||
| 94ccd8f8b6 | |||
| c96ce890d6 | |||
| de55be397b | |||
| 1420714cae | |||
| b15305bc69 | |||
| db25b111e0 | |||
| eeea9932ed | |||
| c710e9a54d | |||
| 62613e7da1 | |||
| 946b597471 | |||
| 691c55bedd | |||
| 8387969467 | |||
| 90d21375c8 | |||
| 86daf2a9dc | |||
| 90473957cb | |||
| 3549c00141 | |||
| 1c1d13545b | |||
| f1b3db89fb | |||
| 8c77baca6f | |||
| 36a40d97a7 | |||
| 1eb9b40607 | |||
| 05c10e3f57 | |||
| 0cf3325655 | |||
| 48525de714 | |||
| 7e15722eab | |||
| 719c209c7b | |||
| d05ad44b84 | |||
| b352c97479 | |||
| 063085a6bb | |||
| b4bca16109 | |||
| 03770c52fe | |||
| 10fe5cdd5d | |||
| 57d459b917 | |||
| fe26594f12 | |||
| 0d8065fc1f | |||
| a3cdae1e94 | |||
| 29d77b649b | |||
| 9e190cee81 | |||
| 687bfd0f17 | |||
| 7d7df4f749 | |||
| 958dede319 | |||
| 76e814944d | |||
| fbc1d75e9a | |||
| b3c7ce05dc | |||
| feea881e09 | |||
| b978bc4876 | |||
| f84181e7a5 | |||
| 313ae0f86c | |||
| a39c018359 | |||
| a09f57d908 | |||
| 596828cf78 | |||
| 44843aa9cd | |||
| 5f930cc4d1 | |||
| 7defad5d95 | |||
| 707af8a295 | |||
| e0fb9ffbb0 | |||
| d614de6f5e | |||
| 86b94b4723 | |||
| 5070300050 | |||
| 1919546441 | |||
| 1c3cd8d44b | |||
| 292296266f | |||
| a3e8848bc8 | |||
| ced146fc63 | |||
| 6ed79f6a0d | |||
| 15e4d86e92 | |||
| 5a29a7d2a3 | |||
| 034740ce46 | |||
| a2847246e6 | |||
| 04479ad660 | |||
| 35d2fff593 | |||
| 2b93fe9a30 | |||
| 5c33a2bd5c | |||
| a28f113105 | |||
| eb7976a2ef | |||
| 15db1c0c30 | |||
| 2b83c95869 | |||
| 273d5596d7 | |||
| b423dab152 | |||
| 1b104bcf29 | |||
| 5cad9a8c44 | |||
| ec8a475a2c | |||
| e656cb8118 | |||
| 7f2a0b6630 | |||
| f9bf40a771 | |||
| 96360619e1 | |||
| 4cefc21819 | |||
| 192a56ee15 | |||
| cf16472dd0 | |||
| b2fb2d5821 | |||
| c939eda7bb | |||
| 428e059895 | |||
| 1b4ea2e9c6 | |||
| d60bfa238f | |||
| cc98f7da36 | |||
| 87cdc22990 | |||
| 60f6ea3252 | |||
| 86cffae001 | |||
| 77cec0e338 | |||
| ee85bb9a1b | |||
| bf0b85afd1 | |||
| 5473696491 | |||
| 0bc59a82ba | |||
| a8430d8ecf | |||
| 9672b2ec23 | |||
| 60447f073f | |||
| 62329c104c | |||
| 536884bb46 | |||
| efb213fb9f | |||
| 02b43c9375 | |||
| 9a2679efaa | |||
| 00a396c3e7 | |||
| b4272c306d | |||
| b876605e93 | |||
| 7f896a9f40 | |||
| a96874ac55 | |||
| 1f8d3ed911 | |||
| 56be0ef4be | |||
| 67079545b3 | |||
| 54b8811843 | |||
| 0bbfe20089 | |||
| 667822ca08 | |||
| 051b8c4b63 | |||
| c2f5e7eb45 | |||
| 45ebf7b5c6 | |||
| 2ccdcf8a92 | |||
| 88d9fd42c4 | |||
| 6d81636d1e | |||
| 22fb456d13 | |||
| 62c9666013 | |||
| e1b2f940ad | |||
| d2c0ec12f3 | |||
| 61bdbdb5d6 | |||
| a28ca46460 | |||
| 719179c1c2 | |||
| 1851e0c02d | |||
| 5d7ae16b4a | |||
| 3bb8cb52f8 | |||
| 3d4d3b000e | |||
| e3e97db101 | |||
| 057b8d9e74 | |||
| 003e42a2ae | |||
| baf932dd8b | |||
| e2acab67e1 | |||
| 095b171db3 | |||
| 9d36d7119b | |||
| 507bcbd332 | |||
| 71a15b7202 | |||
| 1718a123e8 | |||
| b5c1046dca | |||
| 608481df38 | |||
| 4aab5da7ae | |||
| 79650cf318 | |||
| 170544f68d | |||
| 9bc26b0068 | |||
| 9765319027 | |||
| 3b1c7dc5e1 | |||
| 2a50d0856b | |||
| 23c0fcada6 | |||
| aa499eab61 | |||
| 985324ac12 | |||
| 3c89907403 | |||
| 953487bb65 | |||
| 677891d0ff | |||
| 8d2a7b00dc | |||
| d4d4f3c082 | |||
| c26e8a69ca | |||
| 1c251a308a | |||
| c8015898ff | |||
| a1e36594c2 | |||
| 043bc754ae | |||
| 472d9dd467 | |||
| f6a3662b0e | |||
| fb777d0c3a | |||
| 2a3ee968ff | |||
| 0b2661ad45 | |||
| 1dfe68e9f3 | |||
| 64b824671a | |||
| 4940d85b29 | |||
| d458748a7a | |||
| 7f346ef606 | |||
| ca544dbbae | |||
| 5704a07b68 | |||
| 5b1739246d | |||
| 8ead33bc59 | |||
| 2f3792a3ee | |||
| 86d306632d | |||
| cc79c2cb49 | |||
| 26b3b13bc4 | |||
| bc20e65aae | |||
| d458bf6be3 | |||
| 58375f06e5 | |||
| cd39853dc8 | |||
| c94a44aeb8 | |||
| 237d7c1ec1 | |||
| a80b67fdc9 | |||
| de44c57ada | |||
| 448356f534 | |||
| 2b16b0fc7c | |||
| 22515c8c29 | |||
| bf050a2d5e | |||
| 74065714cb | |||
| b95a39dc10 | |||
| bcdd202ef9 | |||
| 20ce2ead75 | |||
| a81b9344cf | |||
| a7fe004344 | |||
| 66e8f9865a | |||
| 1cb4413869 | |||
| be6dbefe7c | |||
| f655297052 | |||
| 232c797507 | |||
| 25d4a9c7a6 | |||
| 65e3111e35 | |||
| 048d995922 | |||
| dcac27e072 | |||
| b754ad5248 | |||
| 321c7120df | |||
| 4b85c9e2df | |||
| 62aec34507 | |||
| 427487c598 | |||
| ccb8c43961 | |||
| fee892f9a2 | |||
| 9b990728f6 | |||
| 9b490c9552 | |||
| 133effef0e | |||
| 15706a7af8 | |||
| 2a15faf12e | |||
| 9149792846 | |||
| c90c53c878 | |||
| e6387738ce | |||
| 3882690ee1 | |||
| 4c049d0d75 | |||
| e070af3163 | |||
| 1c35a91d13 | |||
| 4c20e79d1f | |||
| 669f46d6a1 | |||
| ea89a9f706 | |||
| 918985449b | |||
| 18ab93a9f6 | |||
| a6a8363527 | |||
| 90edb0903d | |||
| ba1274ff3a | |||
| 60f91bff50 | |||
| ef2e9406c7 | |||
| 3d6783597f | |||
| 7cab32f71b | |||
| 78a72b42b0 |
@@ -49,7 +49,10 @@ body:
|
||||
label: Installation source
|
||||
multiple: false
|
||||
options:
|
||||
- Binary from GitHub / official Telegram source
|
||||
- Binary from GitHub / @AyuGramReleases channel
|
||||
- Arch Linux (ayugram-desktop / ayugram-desktop-git)
|
||||
- Arch Linux (ayugram-desktop-bin)
|
||||
- flatpak by 0FL01
|
||||
- Other (unofficial) source
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -7,5 +7,5 @@ contact_links:
|
||||
url: https://bugs.telegram.org
|
||||
about: Any bug report or feature request not about Telegram Desktop.
|
||||
- name: Question
|
||||
url: https://t.me/TelegramDesktopTalk
|
||||
url: https://t.me/ayugramchat
|
||||
about: Ask a question.
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
name: Docker.
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/docker.yml'
|
||||
- 'Telegram/build/docker/centos_env/**'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref_name == github.event.repository.default_branch
|
||||
|
||||
env:
|
||||
IMAGE_TAG: ghcr.io/${{ github.repository }}/centos_env:latest
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: First set up.
|
||||
run: |
|
||||
sudo apt update
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
|
||||
|
||||
- name: Free up some disk space.
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
with:
|
||||
tool-cache: true
|
||||
|
||||
- name: Docker image build.
|
||||
run: |
|
||||
cd Telegram/build/docker/centos_env
|
||||
poetry install
|
||||
DEBUG= LTO= poetry run gen_dockerfile | DOCKER_BUILDKIT=1 docker build -t $IMAGE_TAG -
|
||||
|
||||
- name: Push the Docker image.
|
||||
run: docker push $IMAGE_TAG
|
||||
@@ -54,5 +54,16 @@ stage
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
|
||||
# Local configuration files
|
||||
settings.local.json
|
||||
*.local.json
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Cursor IDE local settings (but keep .cursor/rules/)
|
||||
.cursor/*
|
||||
!.cursor/rules/
|
||||
|
||||
# Project specific
|
||||
Telegram/SourceFiles/_other/packer_private.h
|
||||
|
||||
@@ -91,3 +91,6 @@
|
||||
[submodule "Telegram/ThirdParty/xdg-desktop-portal"]
|
||||
path = Telegram/ThirdParty/xdg-desktop-portal
|
||||
url = https://github.com/flatpak/xdg-desktop-portal.git
|
||||
[submodule "Telegram/lib_icu"]
|
||||
path = Telegram/lib_icu
|
||||
url = https://github.com/AyuGram/lib_icu.git
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- Кастомизация шрифта
|
||||
- Режим Стримера
|
||||
- Локальный телеграм премиум
|
||||
- Переводчик
|
||||
- Превью медиа и быстрая реакция при сильном нажатии на тачпад (macOS)
|
||||
- Улучшенный вид
|
||||
|
||||
@@ -91,7 +92,9 @@ brew install --cask ayugram
|
||||
|
||||
### Любой другой Линукс дистрибутив
|
||||
|
||||
Следуйте [официальному руководству](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||
Flatpak: https://github.com/0FL01/AyuGramDesktop-flatpak
|
||||
|
||||
Или следуйте [официальному руководству](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||
|
||||
### Примечания для Windows
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- Font customization
|
||||
- Streamer mode
|
||||
- Local Telegram Premium
|
||||
- Translator
|
||||
- Media preview & quick reaction on force click (macOS)
|
||||
- Enhanced appearance
|
||||
|
||||
@@ -92,7 +93,9 @@ See [this repository](https://github.com/ayugram-port/ayugram-desktop) for insta
|
||||
|
||||
### Any other Linux distro
|
||||
|
||||
Follow the [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||
Flatpak: https://github.com/0FL01/AyuGramDesktop-flatpak
|
||||
|
||||
Or follow the [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-linux.md).
|
||||
|
||||
### Remarks for Windows
|
||||
|
||||
@@ -122,6 +125,7 @@ Enjoy using **AyuGram**? Consider sending us a tip!
|
||||
- [JSON for Modern C++](https://github.com/nlohmann/json)
|
||||
- [SQLite](https://github.com/sqlite/sqlite)
|
||||
- [sqlite_orm](https://github.com/fnc12/sqlite_orm)
|
||||
- [androidx sources](https://github.com/androidx/androidx)
|
||||
|
||||
### Icons
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ add_subdirectory(lib_webrtc)
|
||||
add_subdirectory(lib_webview)
|
||||
add_subdirectory(codegen)
|
||||
|
||||
# AyuGram filters
|
||||
add_subdirectory(lib_icu)
|
||||
# AyuGram filters
|
||||
|
||||
get_filename_component(src_loc SourceFiles REALPATH)
|
||||
get_filename_component(res_loc Resources REALPATH)
|
||||
|
||||
@@ -92,6 +96,9 @@ PRIVATE
|
||||
desktop-app::external_openssl
|
||||
desktop-app::external_openal
|
||||
desktop-app::external_xxhash
|
||||
|
||||
# AyuGram filters
|
||||
ayugram::lib_icu
|
||||
)
|
||||
|
||||
set(ayugram_files
|
||||
@@ -107,7 +114,6 @@ set(ayugram_files
|
||||
ayu/ayu_lang.h
|
||||
ayu/ayu_infra.cpp
|
||||
ayu/ayu_infra.h
|
||||
ayu/ayu_constants.h
|
||||
ayu/utils/ayu_mapper.cpp
|
||||
ayu/utils/ayu_mapper.h
|
||||
ayu/utils/qt_key_modifiers_extended.h
|
||||
@@ -127,8 +133,34 @@ set(ayugram_files
|
||||
ayu/ui/ayu_logo.h
|
||||
ayu/ui/utils/ayu_profile_values.cpp
|
||||
ayu/ui/utils/ayu_profile_values.h
|
||||
ayu/ui/utils/color_cut_quantizer.cpp
|
||||
ayu/ui/utils/color_cut_quantizer.h
|
||||
ayu/ui/utils/color_utils.cpp
|
||||
ayu/ui/utils/color_utils.h
|
||||
ayu/ui/utils/palette.cpp
|
||||
ayu/ui/utils/palette.h
|
||||
ayu/ui/settings/settings_appearance.cpp
|
||||
ayu/ui/settings/settings_appearance.h
|
||||
ayu/ui/settings/settings_ayu_utils.cpp
|
||||
ayu/ui/settings/settings_ayu_utils.h
|
||||
ayu/ui/settings/settings_chats.cpp
|
||||
ayu/ui/settings/settings_chats.h
|
||||
ayu/ui/settings/settings_general.cpp
|
||||
ayu/ui/settings/settings_general.h
|
||||
ayu/ui/settings/settings_ayu.cpp
|
||||
ayu/ui/settings/settings_ayu.h
|
||||
ayu/ui/settings/settings_filters.cpp
|
||||
ayu/ui/settings/settings_filters.h
|
||||
ayu/ui/settings/settings_main.cpp
|
||||
ayu/ui/settings/settings_main.h
|
||||
ayu/ui/settings/settings_other.cpp
|
||||
ayu/ui/settings/settings_other.h
|
||||
ayu/ui/settings/filters/settings_filters_list.cpp
|
||||
ayu/ui/settings/filters/settings_filters_list.h
|
||||
ayu/ui/settings/filters/per_dialog_filter.cpp
|
||||
ayu/ui/settings/filters/per_dialog_filter.h
|
||||
ayu/ui/settings/filters/edit_filter.cpp
|
||||
ayu/ui/settings/filters/edit_filter.h
|
||||
ayu/ui/context_menu/context_menu.cpp
|
||||
ayu/ui/context_menu/context_menu.h
|
||||
ayu/ui/context_menu/menu_item_subtext.cpp
|
||||
@@ -143,14 +175,22 @@ set(ayugram_files
|
||||
ayu/ui/boxes/edit_mark_box.h
|
||||
ayu/ui/boxes/font_selector.cpp
|
||||
ayu/ui/boxes/font_selector.h
|
||||
ayu/ui/boxes/import_filters_box.cpp
|
||||
ayu/ui/boxes/import_filters_box.h
|
||||
ayu/ui/boxes/theme_selector_box.cpp
|
||||
ayu/ui/boxes/theme_selector_box.h
|
||||
ayu/ui/boxes/message_shot_box.cpp
|
||||
ayu/ui/boxes/message_shot_box.h
|
||||
ayu/ui/boxes/donate_qr_box.cpp
|
||||
ayu/ui/boxes/donate_qr_box.h
|
||||
ayu/ui/boxes/donate_info_box.cpp
|
||||
ayu/ui/boxes/donate_info_box.h
|
||||
ayu/ui/components/image_view.cpp
|
||||
ayu/ui/components/image_view.h
|
||||
ayu/ui/components/icon_picker.cpp
|
||||
ayu/ui/components/icon_picker.h
|
||||
ayu/ui/components/now_playing.cpp
|
||||
ayu/ui/components/now_playing.h
|
||||
ayu/libs/json.hpp
|
||||
ayu/libs/json_ext.hpp
|
||||
ayu/libs/sqlite/sqlite3.c
|
||||
@@ -164,17 +204,40 @@ set(ayugram_files
|
||||
ayu/features/streamer_mode/platform/streamer_mode_mac.h
|
||||
ayu/features/streamer_mode/streamer_mode.cpp
|
||||
ayu/features/streamer_mode/streamer_mode.h
|
||||
ayu/features/messageshot/message_shot.cpp
|
||||
ayu/features/messageshot/message_shot.h
|
||||
ayu/features/message_shot/message_shot.cpp
|
||||
ayu/features/message_shot/message_shot.h
|
||||
ayu/features/forward/ayu_forward.cpp
|
||||
ayu/features/forward/ayu_forward.h
|
||||
ayu/features/forward/ayu_sync.cpp
|
||||
ayu/features/forward/ayu_sync.h
|
||||
ayu/features/translator/ayu_translator.cpp
|
||||
ayu/features/translator/ayu_translator.h
|
||||
ayu/features/translator/html_parser.cpp
|
||||
ayu/features/translator/html_parser.h
|
||||
ayu/features/translator/implementations/google.cpp
|
||||
ayu/features/translator/implementations/google.h
|
||||
ayu/features/translator/implementations/yandex.cpp
|
||||
ayu/features/translator/implementations/yandex.h
|
||||
ayu/features/translator/implementations/telegram.cpp
|
||||
ayu/features/translator/implementations/telegram.h
|
||||
ayu/features/translator/implementations/base.cpp
|
||||
ayu/features/translator/implementations/base.h
|
||||
ayu/features/filters/filters_controller.cpp
|
||||
ayu/features/filters/filters_controller.h
|
||||
ayu/features/filters/filters_cache_controller.cpp
|
||||
ayu/features/filters/filters_cache_controller.h
|
||||
ayu/features/filters/filters_utils.cpp
|
||||
ayu/features/filters/filters_utils.h
|
||||
ayu/features/filters/shadow_ban_utils.cpp
|
||||
ayu/features/filters/shadow_ban_utils.h
|
||||
ayu/data/messages_storage.cpp
|
||||
ayu/data/messages_storage.h
|
||||
ayu/data/entities.h
|
||||
ayu/data/ayu_database.cpp
|
||||
ayu/data/ayu_database.h
|
||||
|
||||
info/profile/info_profile_music_button.cpp
|
||||
info/profile/info_profile_music_button.h
|
||||
)
|
||||
|
||||
target_precompile_headers(Telegram PRIVATE $<$<COMPILE_LANGUAGE:CXX,OBJCXX>:${src_loc}/stdafx.h>)
|
||||
@@ -607,6 +670,8 @@ PRIVATE
|
||||
data/notify/data_notify_settings.h
|
||||
data/notify/data_peer_notify_settings.cpp
|
||||
data/notify/data_peer_notify_settings.h
|
||||
data/notify/data_peer_notify_volume.cpp
|
||||
data/notify/data_peer_notify_volume.h
|
||||
data/stickers/data_custom_emoji.cpp
|
||||
data/stickers/data_custom_emoji.h
|
||||
data/stickers/data_stickers_set.cpp
|
||||
@@ -696,6 +761,7 @@ PRIVATE
|
||||
data/data_peer_bot_command.h
|
||||
data/data_peer_bot_commands.cpp
|
||||
data/data_peer_bot_commands.h
|
||||
data/data_peer_common.h
|
||||
data/data_peer_id.cpp
|
||||
data/data_peer_id.h
|
||||
data/data_peer_values.cpp
|
||||
@@ -717,6 +783,8 @@ PRIVATE
|
||||
data/data_report.h
|
||||
data/data_saved_messages.cpp
|
||||
data/data_saved_messages.h
|
||||
data/data_saved_music.cpp
|
||||
data/data_saved_music.h
|
||||
data/data_saved_sublist.cpp
|
||||
data/data_saved_sublist.h
|
||||
data/data_search_controller.cpp
|
||||
@@ -729,6 +797,7 @@ PRIVATE
|
||||
data/data_shared_media.h
|
||||
data/data_sparse_ids.cpp
|
||||
data/data_sparse_ids.h
|
||||
data/data_star_gift.cpp
|
||||
data/data_star_gift.h
|
||||
data/data_statistics.h
|
||||
data/data_stories.cpp
|
||||
@@ -791,6 +860,8 @@ PRIVATE
|
||||
dialogs/dialogs_search_from_controllers.h
|
||||
dialogs/dialogs_search_tags.cpp
|
||||
dialogs/dialogs_search_tags.h
|
||||
dialogs/dialogs_search_posts.cpp
|
||||
dialogs/dialogs_search_posts.h
|
||||
dialogs/dialogs_top_bar_suggestion.cpp
|
||||
dialogs/dialogs_top_bar_suggestion.h
|
||||
dialogs/dialogs_widget.cpp
|
||||
@@ -893,6 +964,8 @@ PRIVATE
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_save_document_action.cpp
|
||||
history/view/media/history_view_save_document_action.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_similar_channels.cpp
|
||||
@@ -956,6 +1029,8 @@ PRIVATE
|
||||
history/view/history_view_fake_items.h
|
||||
history/view/history_view_group_call_bar.cpp
|
||||
history/view/history_view_group_call_bar.h
|
||||
history/view/history_view_group_members_widget.cpp
|
||||
history/view/history_view_group_members_widget.h
|
||||
history/view/history_view_item_preview.h
|
||||
history/view/history_view_list_widget.cpp
|
||||
history/view/history_view_list_widget.h
|
||||
@@ -1090,6 +1165,8 @@ PRIVATE
|
||||
info/media/info_media_widget.h
|
||||
info/members/info_members_widget.cpp
|
||||
info/members/info_members_widget.h
|
||||
info/peer_gifts/info_peer_gifts_collections.cpp
|
||||
info/peer_gifts/info_peer_gifts_collections.h
|
||||
info/peer_gifts/info_peer_gifts_common.cpp
|
||||
info/peer_gifts/info_peer_gifts_common.h
|
||||
info/peer_gifts/info_peer_gifts_widget.cpp
|
||||
@@ -1124,6 +1201,11 @@ PRIVATE
|
||||
info/reactions_list/info_reactions_list_widget.h
|
||||
info/requests_list/info_requests_list_widget.cpp
|
||||
info/requests_list/info_requests_list_widget.h
|
||||
info/saved/info_saved_music_common.h
|
||||
info/saved/info_saved_music_provider.cpp
|
||||
info/saved/info_saved_music_provider.h
|
||||
info/saved/info_saved_music_widget.cpp
|
||||
info/saved/info_saved_music_widget.h
|
||||
info/saved/info_saved_sublists_widget.cpp
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
@@ -1140,6 +1222,9 @@ PRIVATE
|
||||
info/statistics/info_statistics_tag.h
|
||||
info/statistics/info_statistics_widget.cpp
|
||||
info/statistics/info_statistics_widget.h
|
||||
info/stories/info_stories_albums.cpp
|
||||
info/stories/info_stories_albums.h
|
||||
info/stories/info_stories_common.h
|
||||
info/stories/info_stories_inner_widget.cpp
|
||||
info/stories/info_stories_inner_widget.h
|
||||
info/stories/info_stories_provider.cpp
|
||||
@@ -1194,6 +1279,8 @@ PRIVATE
|
||||
inline_bots/inline_results_widget.h
|
||||
intro/intro_code.cpp
|
||||
intro/intro_code.h
|
||||
intro/intro_email.cpp
|
||||
intro/intro_email.h
|
||||
intro/intro_password_check.cpp
|
||||
intro/intro_password_check.h
|
||||
intro/intro_phone.cpp
|
||||
@@ -1347,6 +1434,8 @@ PRIVATE
|
||||
menu/menu_antispam_validator.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_item_rate_transcribe_session.cpp
|
||||
menu/menu_item_rate_transcribe_session.h
|
||||
menu/menu_mute.cpp
|
||||
menu/menu_mute.h
|
||||
menu/menu_send.cpp
|
||||
@@ -1380,6 +1469,8 @@ PRIVATE
|
||||
mtproto/special_config_request.cpp
|
||||
mtproto/special_config_request.h
|
||||
mtproto/type_utils.h
|
||||
overview/overview_checkbox.cpp
|
||||
overview/overview_checkbox.h
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
@@ -1500,10 +1591,6 @@ PRIVATE
|
||||
platform/platform_window_title.h
|
||||
profile/profile_back_button.cpp
|
||||
profile/profile_back_button.h
|
||||
profile/profile_block_group_members.cpp
|
||||
profile/profile_block_group_members.h
|
||||
profile/profile_block_peer_list.cpp
|
||||
profile/profile_block_peer_list.h
|
||||
profile/profile_block_widget.cpp
|
||||
profile/profile_block_widget.h
|
||||
profile/profile_cover_drop_area.cpp
|
||||
@@ -1698,8 +1785,6 @@ PRIVATE
|
||||
ui/text/format_song_document_name.h
|
||||
ui/widgets/expandable_peer_list.cpp
|
||||
ui/widgets/expandable_peer_list.h
|
||||
ui/widgets/label_with_custom_emoji.cpp
|
||||
ui/widgets/label_with_custom_emoji.h
|
||||
ui/widgets/chat_filters_tabs_strip.cpp
|
||||
ui/widgets/chat_filters_tabs_strip.h
|
||||
ui/widgets/peer_bubble.cpp
|
||||
@@ -1732,6 +1817,8 @@ PRIVATE
|
||||
window/window_adaptive.h
|
||||
window/window_chat_preview.cpp
|
||||
window/window_chat_preview.h
|
||||
window/window_chat_switch_process.cpp
|
||||
window/window_chat_switch_process.h
|
||||
window/window_connecting_widget.cpp
|
||||
window/window_connecting_widget.h
|
||||
window/window_controller.cpp
|
||||
@@ -2008,6 +2095,7 @@ PRIVATE
|
||||
TDESKTOP_API_ID=${TDESKTOP_API_ID}
|
||||
TDESKTOP_API_HASH=${TDESKTOP_API_HASH}
|
||||
G_LOG_DOMAIN="Telegram"
|
||||
U_STATIC_IMPLEMENTATION=1 # AyuGram: do not use __declspec(dllimport) when including icu headers
|
||||
)
|
||||
|
||||
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M65.0957 43.2571C61.089 59.3287 44.8111 69.1096 28.7376 65.1018C12.6708 61.0951 2.88998 44.8162 6.89846 28.7459C10.9034 12.6726 27.1813 2.89099 43.2498 6.8977C59.3222 10.9044 69.1024 27.1851 65.0953 43.2574L65.0956 43.2571H65.0957Z" fill="#F7931A"/>
|
||||
<path d="M49.2257 31.7258C49.8228 27.7335 46.7833 25.5875 42.627 24.1558L43.9753 18.7479L40.6833 17.9276L39.3707 23.1932C38.5053 22.9773 37.6165 22.7739 36.7332 22.5723L38.0553 17.2719L34.7653 16.4517L33.4162 21.8579C32.7001 21.6948 31.9967 21.5337 31.3142 21.3639L31.318 21.3469L26.7783 20.2132L25.9026 23.7293C25.9026 23.7293 28.3449 24.2891 28.2935 24.3236C29.6266 24.6563 29.8676 25.5388 29.8276 26.2382L28.2917 32.3992C28.3835 32.4225 28.5026 32.4562 28.634 32.509C28.5241 32.4818 28.4073 32.452 28.286 32.423L26.1332 41.0536C25.9703 41.4586 25.5568 42.0664 24.6248 41.8356C24.6577 41.8834 22.2321 41.2385 22.2321 41.2385L20.5977 45.0068L24.8817 46.0747C25.6786 46.2746 26.4596 46.4837 27.2287 46.6803L25.8665 52.1504L29.1547 52.9706L30.5037 47.5587C31.402 47.8026 32.2738 48.0275 33.1272 48.2395L31.7827 53.626L35.0749 54.4463L36.437 48.9865C42.0505 50.0489 46.2715 49.6206 48.0482 44.5431C49.4798 40.4552 47.9769 38.0972 45.0236 36.5596C47.1746 36.0635 48.7948 34.6488 49.2268 31.7263L49.2258 31.7255L49.2257 31.7258ZM41.7042 42.2729C40.6868 46.3608 33.804 44.151 31.5724 43.5969L33.3802 36.35C35.6116 36.9071 42.7675 38.0095 41.7043 42.2729H41.7042ZM42.7223 31.6666C41.7942 35.385 36.0655 33.4959 34.2072 33.0327L35.8462 26.4602C37.7045 26.9235 43.6891 27.788 42.7226 31.6666H42.7223Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5796 41.8413L22.9288 6H38.8335L35.6272 17.1111C35.5954 17.1746 35.5637 17.2381 35.532 17.3016L27.0875 46.6349H34.9605C31.6589 54.8571 29.0875 61.3016 27.2462 65.9683C12.7066 65.8095 8.64307 55.3968 12.1986 43.0794M27.3097 66L46.4843 38.4127H38.3574L45.4367 20.7302C57.5637 22 63.278 31.5556 59.9129 43.1111C56.3256 55.5238 41.7859 66 27.6272 66C27.5002 66 27.405 66 27.3097 66Z" fill="url(#paint0_linear_1491_36)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1491_36" x1="44.5725" y1="13.793" x2="24.0992" y2="83.6122" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EF7829"/>
|
||||
<stop offset="0.0518954" stop-color="#F07529"/>
|
||||
<stop offset="0.3551" stop-color="#F0672B"/>
|
||||
<stop offset="0.6673" stop-color="#F15E2C"/>
|
||||
<stop offset="1" stop-color="#F15A2C"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 898 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M36.414 28.1822V6L18 36.5512L36.414 28.1822Z" fill="#8A92B2"/>
|
||||
<path d="M36.414 47.4398V28.1822L18 36.5512L36.414 47.4398ZM36.414 28.1822L54.8279 36.5512L36.414 6V28.1822Z" fill="#62688F"/>
|
||||
<path d="M36.4141 28.1824V47.44L54.828 36.5513L36.4141 28.1824Z" fill="#454A75"/>
|
||||
<path d="M36.414 50.927L18 40.0496L36.414 66.0001V50.927Z" fill="#8A92B2"/>
|
||||
<path d="M54.8393 40.0496L36.4141 50.927V66.0001L54.8393 40.0496Z" fill="#62688F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 544 B |
@@ -0,0 +1,19 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.7481 47.8879C16.1101 47.5259 16.608 47.3147 17.1359 47.3147H65.0164C65.8914 47.3147 66.3288 48.3707 65.7103 48.9892L56.2519 58.4476C55.8899 58.8096 55.3921 59.0208 54.8641 59.0208H6.98358C6.10864 59.0208 5.67117 57.9649 6.28966 57.3464L15.7481 47.8879Z" fill="url(#paint0_linear_1498_44)"/>
|
||||
<path d="M15.7481 12.5732C16.1252 12.2112 16.623 12 17.1359 12H65.0164C65.8914 12 66.3288 13.056 65.7103 13.6745L56.2519 23.1329C55.8899 23.4949 55.3921 23.7061 54.8641 23.7061H6.98358C6.10864 23.7061 5.67117 22.6502 6.28966 22.0317L15.7481 12.5732Z" fill="url(#paint1_linear_1498_44)"/>
|
||||
<path d="M56.2519 30.1174C55.8899 29.7554 55.3921 29.5442 54.8641 29.5442H6.98358C6.10864 29.5442 5.67117 30.6002 6.28966 31.2186L15.7481 40.6771C16.1101 41.0391 16.608 41.2503 17.1359 41.2503H65.0164C65.8914 41.2503 66.3288 40.1944 65.7103 39.5759L56.2519 30.1174Z" fill="url(#paint2_linear_1498_44)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1498_44" x1="60.4424" y1="6.34998" x2="27.3053" y2="69.8209" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1498_44" x1="45.9531" y1="-1.21486" x2="12.816" y2="62.256" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1498_44" x1="53.1517" y1="2.54346" x2="20.0145" y2="66.0144" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="80" height="80" rx="40" fill="#E83030"/>
|
||||
<path d="M39.999 27.5657C50.4546 16.7028 63.2343 24.7157 63.2344 36.0149C63.2344 47.3141 53.8952 53.3355 47.0586 58.7249C44.6462 60.6266 42.3226 62.4172 39.999 62.4172C37.6755 62.4172 35.3519 60.6266 32.9395 58.7249C26.1029 53.3355 16.7637 47.3141 16.7637 36.0149C16.7638 24.7157 29.5435 16.7028 39.999 27.5657ZM48.2627 35.677C48.7923 34.089 47.2803 32.5775 45.6924 33.1077L30.1621 38.2932C28.6596 38.7949 28.6067 41.0213 30.0391 41.6975C32.1559 42.6961 34.5207 44.0094 35.9287 45.4172C37.3438 46.8324 38.6704 49.2208 39.6777 51.3528C40.354 52.7834 42.5744 52.728 43.0752 51.2268L48.2627 35.677Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 769 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1491_40)">
|
||||
<path d="M36 66C52.5686 66 66 52.5686 66 36C66 19.4314 52.5686 6 36 6C19.4314 6 6 19.4314 6 36C6 52.5686 19.4314 66 36 66Z" fill="#0098EA"/>
|
||||
<path d="M46.2396 22.7437H25.7521C21.9851 22.7437 19.5976 26.807 21.4927 30.0919L34.1369 52.0078C34.962 53.4388 37.0297 53.4388 37.8548 52.0078L50.5015 30.0919C52.3941 26.8123 50.0065 22.7437 46.2422 22.7437H46.2396ZM34.1266 45.4355L31.3729 40.1062L24.7285 28.2226C24.2902 27.462 24.8316 26.4873 25.7495 26.4873H34.124V45.4381L34.1266 45.4355ZM47.258 28.22L40.6162 40.1087L37.8625 45.4355V26.4848H46.237C47.1549 26.4848 47.6963 27.4594 47.258 28.22Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1491_40">
|
||||
<rect width="60" height="60" fill="white" transform="translate(6 6)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 879 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M63.2493 24.0756C60.4368 21.4787 56.5462 17.5131 53.3775 14.7007L53.19 14.5694C52.878 14.3189 52.5263 14.1224 52.1494 13.9882C44.5089 12.5632 8.94997 5.91642 8.25623 6.0008C8.06184 6.02802 7.87604 6.0985 7.71249 6.20704L7.53437 6.34767C7.31504 6.57041 7.14845 6.83951 7.04687 7.13516L7 7.25703V7.92265V8.02577C11.0031 19.1725 26.8091 55.6876 29.9216 64.2563C30.1091 64.8375 30.4653 65.9438 31.1309 66H31.2809C31.6372 66 33.1559 63.9938 33.1559 63.9938C33.1559 63.9938 60.3055 31.0692 63.0524 27.563C63.4079 27.1311 63.7218 26.6666 63.9899 26.1755C64.0583 25.7913 64.026 25.396 63.8962 25.028C63.7664 24.66 63.5435 24.3318 63.2493 24.0756ZM40.1214 27.9099L51.7088 18.3006L58.5056 24.563L40.1214 27.9099ZM35.6215 27.2818L15.6718 10.932L47.9495 16.885L35.6215 27.2818ZM37.4215 31.5661L57.8399 28.2755L34.4965 56.4001L37.4215 31.5661ZM12.9624 12.5632L33.9528 30.3755L30.9153 56.4189L12.9624 12.5632Z" fill="#FF060A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" fill="white" fill-opacity="0.1"/>
|
||||
<path d="M10.1312 12C10.1312 10.9679 10.9679 10.1312 12 10.1312C13.0321 10.1312 13.8687 10.9679 13.8687 12C13.8687 13.0321 13.0321 13.8687 12 13.8687C10.9679 13.8687 10.1312 13.0321 10.1312 12Z" fill="white" fill-opacity="0.3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 17.75C15.1756 17.75 17.75 15.1756 17.75 12C17.75 8.82436 15.1756 6.25 12 6.25C8.82436 6.25 6.25 8.82436 6.25 12C6.25 15.1756 8.82436 17.75 12 17.75ZM12 9.26875C10.4916 9.26875 9.26875 10.4916 9.26875 12C9.26875 13.5084 10.4916 14.7312 12 14.7312C13.5084 14.7312 14.7312 13.5084 14.7312 12C14.7312 10.4916 13.5084 9.26875 12 9.26875Z" fill="white" fill-opacity="0.2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 816 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Mini / mini_ton_bold</title>
|
||||
<g id="Icon-/-Mini-/-mini_ton_bold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M12.5980305,2.7875 C13.2201476,2.7875 13.7244732,3.29182558 13.7244732,3.9139427 C13.7244732,4.11165014 13.6724374,4.30587532 13.5735947,4.47710133 L9.21676388,12.0244744 C8.80179975,12.7433201 7.88266529,12.9896647 7.16381961,12.5747006 C6.92829269,12.4387393 6.73407151,12.2414175 6.60185728,12.0037668 L2.40584723,4.46158062 C2.10339516,3.91793325 2.29892259,3.23203413 2.84256996,2.92958206 C3.01005587,2.83640316 3.19854713,2.7875 3.39020787,2.7875 L12.5980305,2.7875 Z M7.24956057,4.2875 L4.025,4.2875 L7.24956057,10.0835 L7.24956057,4.2875 Z M11.95,4.2875 L8.74956057,4.2875 L8.74956057,9.8255 L11.95,4.2875 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level10_inner</title>
|
||||
<g id="Badge-/-level10_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M23,15 L49,15 C53.418278,15 57,18.581722 57,23 L57,46.8429984 C57,48.7715399 55.8908719,50.5281562 54.1496679,51.357301 L37.2898007,59.3858092 C36.4738291,59.7743671 35.5261709,59.7743671 34.7101993,59.3858092 L17.8503321,51.357301 C16.1091281,50.5281562 15,48.7715399 15,46.8429984 L15,23 C15,18.581722 18.581722,15 23,15 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 714 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level1_inner</title>
|
||||
<g id="Badge-/-level1_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M53,45 C54.6943359,39.9169922 55.1925159,32.4416725 54.49454,22.5740409 C54.2708931,19.4342396 51.6589867,17.0009114 48.5112305,17 L23.4887695,17 C20.3410133,17.0009114 17.7291069,19.4342396 17.50546,22.5740409 C16.8074841,32.4416725 17.3056641,39.9169922 19,45 C20.9999787,50.9999361 32.8055802,57.9438271 36.0181519,58.0000467 C39.2307237,58.0555136 50.70353,51.8894101 53,45 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 767 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level20_inner</title>
|
||||
<g id="Badge-/-level20_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M17.5067188,13.1565216 L35.4932812,17.8672879 C35.8254913,17.9542953 36.1745087,17.9542953 36.5067188,17.8672879 L54.4932812,13.1565216 C55.5618111,12.8766685 56.6548914,13.5160174 56.9347444,14.5845472 C56.9780693,14.7499693 57,14.9202646 57,15.091266 L57,50 C57,53.8659932 53.8659932,57 50,57 L22,57 C18.1340068,57 15,53.8659932 15,50 L15,15.091266 C15,13.9866965 15.8954305,13.091266 17,13.091266 C17.1710014,13.091266 17.3412968,13.1131968 17.5067188,13.1565216 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 857 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level2_inner</title>
|
||||
<g id="Badge-/-level2_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M28,15 L44,15 C51.1797017,15 57,20.8202983 57,28 L57,44 C57,51.1797017 51.1797017,57 44,57 L28,57 C20.8202983,57 15,51.1797017 15,44 L15,28 C15,20.8202983 20.8202983,15 28,15 Z" id="Rectangle" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 569 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level30_inner</title>
|
||||
<g id="Badge-/-level30_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M17.5067188,13.1565216 L35.4932812,17.8672879 C35.8254913,17.9542953 36.1745087,17.9542953 36.5067188,17.8672879 L54.4932812,13.1565216 C55.5618111,12.8766685 56.6548914,13.5160174 56.9347444,14.5845472 C56.9780693,14.7499693 57,14.9202646 57,15.091266 L57,47.2740807 C57,49.2380496 55.850227,51.0201354 54.0608393,51.8296203 L37.2365036,59.4406293 C36.4505105,59.7961976 35.5494895,59.7961976 34.7634964,59.4406293 L17.9391607,51.8296203 C16.149773,51.0201354 15,49.2380496 15,47.2740807 L15,15.091266 C15,13.9866965 15.8954305,13.091266 17,13.091266 C17.1710014,13.091266 17.3412968,13.1131968 17.5067188,13.1565216 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1009 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level3_inner</title>
|
||||
<g id="Badge-/-level3_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M35.8544323,16.5 C42.5603579,16.5 48.9772339,17.3142695 55.1127909,18.9403855 C57.8720367,19.6721748 59.5984053,22.4098874 59.0695564,25.2151096 L58.9161688,25.9952446 C57.6492744,32.2345248 55.3583898,38.2325914 52.0339947,44.0033008 C48.4623738,50.2031617 44.2319724,54.8983246 39.3449322,58.1412366 L38.808,58.49 L38.6366496,58.5935691 C37.0148548,59.5241021 35.0268922,59.5572143 33.3750071,58.6812087 L33.244,58.609 L33.1538672,58.5548802 C27.4610226,55.1837021 22.8566119,50.5438551 19.3062949,44.5841668 L18.9660053,44.0033008 C15.6094458,38.1767582 13.4677002,31.8512809 12.5350759,24.997545 C12.1766452,22.3675806 13.7646861,19.874921 16.2717218,19.0736995 L16.531,18.998 L17.3695077,18.774139 C22.9281007,17.3348182 28.7645985,16.5777965 34.8855941,16.5056926 L35.8544323,16.5 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level40_inner</title>
|
||||
<g id="Badge-/-level40_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M13.8209245,17.0037365 L23.6412021,18.7573575 C24.5036156,18.9113599 25.3902576,18.6806984 26.0682874,18.1259467 L34.7335244,11.0362073 C35.4702562,10.4334268 36.5297438,10.4334268 37.2664756,11.0362073 L45.9317126,18.1259467 C46.6097424,18.6806984 47.4963844,18.9113599 48.3587979,18.7573575 L58.1790755,17.0037365 C59.2664441,16.8095635 60.3053389,17.5336417 60.4995119,18.6210104 C60.5408831,18.8526891 60.541039,19.0898514 60.4999724,19.3215843 L55.1703343,49.3959706 C54.4933325,53.2161951 51.17282,56 47.2930717,56 L24.7069283,56 C20.82718,56 17.5066675,53.2161951 16.8296657,49.3959706 L11.5000276,19.3215843 C11.3072843,18.2339613 12.0327276,17.1960193 13.1203506,17.003276 C13.3520835,16.9622094 13.5892458,16.9623653 13.8209245,17.0037365 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level4_inner</title>
|
||||
<g id="Badge-/-level4_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M44.3705856,16.4672101 L55.5327899,27.6294144 C60.1557367,32.2523612 60.1557367,39.7476388 55.5327899,44.3705856 L44.3705856,55.5327899 C39.7476388,60.1557367 32.2523612,60.1557367 27.6294144,55.5327899 L16.4672101,44.3705856 C11.8442633,39.7476388 11.8442633,32.2523612 16.4672101,27.6294144 L27.6294144,16.4672101 C32.2523612,11.8442633 39.7476388,11.8442633 44.3705856,16.4672101 Z" id="Polygon" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 775 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level50_inner</title>
|
||||
<g id="Badge-/-level50_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M13.8457992,17.0081784 L23.6412021,18.7573575 C24.5036156,18.9113599 25.3902576,18.6806984 26.0682874,18.1259467 L34.7335244,11.0362073 C35.4702562,10.4334268 36.5297438,10.4334268 37.2664756,11.0362073 L45.9317126,18.1259467 C46.6097424,18.6806984 47.4963844,18.9113599 48.3587979,18.7573575 L58.1542008,17.0081784 C59.2415694,16.8140055 60.2804642,17.5380836 60.4746372,18.6254523 C60.5169804,18.862574 60.516141,19.1053947 60.4721595,19.342218 L55.2572125,47.4227021 C54.7824054,49.9793556 53.0945991,52.1457247 50.731704,53.2313793 L37.6700007,59.2327024 C36.6099358,59.7197592 35.3900642,59.7197592 34.3299993,59.2327024 L21.268296,53.2313793 C18.9054009,52.1457247 17.2175946,49.9793556 16.7427875,47.4227021 L11.5278405,19.342218 C11.3261547,18.2562177 12.0430333,17.2123419 13.1290336,17.0106561 C13.3658569,16.9666746 13.6086775,16.9658353 13.8457992,17.0081784 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level5_inner</title>
|
||||
<g id="Badge-/-level5_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M24.6347159,52.197233 L15.086894,45.3306104 C13.3626301,44.0905507 12.6070688,41.9044024 13.1995002,39.8696074 L16.4885503,28.5785206 L20.056184,18.2789734 C20.736028,16.3172234 22.5589494,14.9756983 24.6408724,14.9050076 L35.519949,14.536208 L46.4041369,14.5 C48.4875477,14.4932153 50.3596774,15.7663386 51.1123731,17.7018167 L55.0199245,27.7496763 C55.0516847,27.8313442 55.081291,27.9138273 55.1087132,27.9970407 L58.7486546,39.0425771 C59.4120036,41.0555339 58.7334828,43.2666211 57.0537084,44.5658775 L47.7086588,51.7944553 L39.3643082,58.4096688 C37.5845851,59.8205932 35.0761178,59.866608 33.245468,58.5219115 L24.6347159,52.197233 Z" id="Star-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level60_inner</title>
|
||||
<g id="Badge-/-level60_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M25.1630277,52.850407 L17.8273486,50.8900353 C15.0747,50.1544232 13.4412291,47.3453418 14.1759196,44.6106697 L16.1338353,37.322897 C16.3667018,36.4561188 16.3667018,35.5438812 16.1338353,34.677103 L14.1759196,27.3893303 C13.4412291,24.6546582 15.0747,21.8455768 17.8273486,21.1099647 L25.1630277,19.149593 C26.0355037,18.9164343 26.8311982,18.4603155 27.4708076,17.826696 L32.848571,12.499295 C34.8665291,10.500235 38.1334709,10.500235 40.151429,12.499295 L45.5291924,17.826696 C46.1688018,18.4603155 46.9644963,18.9164343 47.8369723,19.149593 L55.1726514,21.1099647 C57.9253,21.8455768 59.5587709,24.6546582 58.8240804,27.3893303 L56.8661647,34.677103 C56.6332982,35.5438812 56.6332982,36.4561188 56.8661647,37.322897 L58.8240804,44.6106697 C59.5587709,47.3453418 57.9253,50.1544232 55.1726514,50.8900353 L47.8369723,52.850407 C46.9644963,53.0835657 46.1688018,53.5396845 45.5291924,54.173304 L40.151429,59.500705 C38.1334709,61.499765 34.8665291,61.499765 32.848571,59.500705 L27.4708076,54.173304 C26.8311982,53.5396845 26.0355037,53.0835657 25.1630277,52.850407 Z" id="Star-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level6_inner</title>
|
||||
<g id="Badge-/-level6_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M56.5,27.2077494 L56.5,44.7922506 C56.5,46.727022 55.4750178,48.5148244 53.8111544,49.4822101 L38.6888456,58.2744607 C37.0249822,59.2418464 34.9750178,59.2418464 33.3111544,58.2744607 L18.1888456,49.4822101 C16.5249822,48.5148244 15.5,46.727022 15.5,44.7922506 L15.5,27.2077494 C15.5,25.272978 16.5249822,23.4851756 18.1888456,22.5177899 L33.3111544,13.7255393 C34.9750178,12.7581536 37.0249822,12.7581536 38.6888456,13.7255393 L53.8111544,22.5177899 C55.4750178,23.4851756 56.5,25.272978 56.5,27.2077494 Z" id="Polygon-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 902 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level70_inner</title>
|
||||
<g id="Badge-/-level70_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M25.6231529,53.6849537 L20.2456512,53.1119864 C17.3639984,52.8049492 15.263688,50.1918606 15.5389909,47.256223 L16.0527382,41.7779796 C16.1648264,40.5827489 15.8804506,39.3837383 15.2449668,38.3721824 L12.332278,33.7357975 C10.7714495,31.2512883 11.4691887,27.9507327 13.8958667,26.3394648 L18.424332,23.3326503 C19.4123422,22.6766303 20.1560647,21.7027674 20.5368804,20.5663865 L22.2823186,15.3578763 C23.2176501,12.5667813 26.188027,11.0641444 28.9387422,11.9905637 L34.0718987,13.7193707 C35.1918391,14.0965579 36.4036217,14.0811813 37.5139749,13.6756934 L42.6031895,11.8171724 C45.3303573,10.821243 48.3366176,12.248041 49.3400252,15.0145349 L51.2125014,20.1771366 C51.621034,21.3035012 52.3883797,22.2581898 53.3921518,22.8889356 L57.9928604,25.779908 C60.4582516,27.3290994 61.2366199,30.6109243 59.7371336,33.1342664 L56.9389167,37.8431184 C56.3284081,38.870485 56.07349,40.0763388 56.2148201,41.2683538 L56.8625953,46.7318584 C57.20972,49.6595979 55.1740691,52.3251686 52.3008327,52.7052309 L46.9390371,53.4144716 C45.7692127,53.5692121 44.6839892,54.1181986 43.8564529,54.9738712 L40.0635069,58.8957776 C38.030973,60.9974176 34.7141896,61.0395051 32.6308086,58.9900928 L28.7429758,55.1656495 C27.8947373,54.3312412 26.7964039,53.8099624 25.6231529,53.6849537 Z" id="Star-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level7_inner</title>
|
||||
<g id="Badge-/-level7_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M47.5119469,15.9725146 L56.9485165,28.1544765 C57.8703305,29.3444749 58.2101938,30.8974518 57.8718989,32.3737956 L54.4087892,47.487062 C54.0704943,48.9634057 53.0908608,50.2024337 51.7472,50.8534058 L37.9922031,57.5173789 C36.6485423,58.1683511 35.087096,58.1604167 33.7498732,57.4958219 L20.0607825,50.6923941 C18.7235597,50.0277993 17.7561016,48.7788774 17.4322729,47.2991691 L14.1172527,32.1514602 C13.793424,30.6717519 14.1484698,29.1223062 15.0818847,27.9417349 L24.637213,15.8562787 C25.570628,14.6757075 26.9808209,13.9925021 28.468599,14 L43.6989186,14.0774536 C45.1866968,14.0850136 46.5901329,14.7825162 47.5119469,15.9725146 Z" id="Polygon-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level80_inner</title>
|
||||
<g id="Badge-/-level80_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M25.4075318,55.0983015 L20.6181753,54.4891523 C18.1878179,54.1800396 16.3367069,52.1599065 16.2405902,49.7118564 L16.0511788,44.887634 C16.0102196,43.84442 15.6446438,42.8400089 15.0054554,42.0145324 L12.0496025,38.1972124 C10.5496559,36.2601145 10.6691726,33.5227316 12.3322407,31.723734 L15.609547,28.1785595 C16.3182481,27.4119333 16.769973,26.4432062 16.9016985,25.4075318 L17.5108477,20.6181753 C17.8199604,18.1878179 19.8400935,16.3367069 22.2881436,16.2405902 L27.112366,16.0511788 C28.15558,16.0102196 29.1599911,15.6446438 29.9854676,15.0054554 L33.8027876,12.0496025 C35.7398855,10.5496559 38.4772684,10.6691726 40.276266,12.3322407 L43.8214405,15.609547 C44.5880667,16.3182481 45.5567938,16.769973 46.5924682,16.9016985 L51.3818247,17.5108477 C53.8121821,17.8199604 55.6632931,19.8400935 55.7594098,22.2881436 L55.9488212,27.112366 C55.9897804,28.15558 56.3553562,29.1599911 56.9945446,29.9854676 L59.9503975,33.8027876 C61.4503441,35.7398855 61.3308274,38.4772684 59.6677593,40.276266 L56.390453,43.8214405 C55.6817519,44.5880667 55.230027,45.5567938 55.0983015,46.5924682 L54.4891523,51.3818247 C54.1800396,53.8121821 52.1599065,55.6632931 49.7118564,55.7594098 L44.887634,55.9488212 C43.84442,55.9897804 42.8400089,56.3553562 42.0145324,56.9945446 L38.1972124,59.9503975 C36.2601145,61.4503441 33.5227316,61.3308274 31.723734,59.6677593 L28.1785595,56.390453 C27.4119333,55.6817519 26.4432062,55.230027 25.4075318,55.0983015 Z" id="Star" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level8_inner</title>
|
||||
<g id="Badge-/-level8_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M27.9852814,15 L44.0147186,15 C45.6060176,15 47.132141,15.632141 48.2573593,16.7573593 L55.2426407,23.7426407 C56.367859,24.867859 57,26.3939824 57,27.9852814 L57,44.0147186 C57,45.6060176 56.367859,47.132141 55.2426407,48.2573593 L48.2573593,55.2426407 C47.132141,56.367859 45.6060176,57 44.0147186,57 L27.9852814,57 C26.3939824,57 24.867859,56.367859 23.7426407,55.2426407 L16.7573593,48.2573593 C15.632141,47.132141 15,45.6060176 15,44.0147186 L15,27.9852814 C15,26.3939824 15.632141,24.867859 16.7573593,23.7426407 L23.7426407,16.7573593 C24.867859,15.632141 26.3939824,15 27.9852814,15 Z" id="Rectangle" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 985 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level90_inner</title>
|
||||
<g id="Badge-/-level90_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M15.8733536,45.5876663 L13.5374788,26.4767308 C13.2739231,24.3204523 14.4193524,22.2383675 16.3756386,21.3177141 L34.3082495,12.8783965 C35.3803169,12.3738678 36.6196831,12.3738678 37.6917505,12.8783965 L55.6243614,21.3177141 C57.5806476,22.2383675 58.7260769,24.3204523 58.4625212,26.4767308 L56.1266464,45.5876663 C55.9615284,46.9385772 55.2580965,48.1641245 54.1775745,48.9834175 L39.003776,60.4887707 C37.2255672,61.8370764 34.7744328,61.8370764 32.996224,60.4887707 L17.8224255,48.9834175 C16.7419035,48.1641245 16.0384716,46.9385772 15.8733536,45.5876663 Z" id="Diamond-Flat" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 960 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Badge / level9_inner</title>
|
||||
<g id="Badge-/-level9_inner" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<circle id="Oval" fill="#FFFFFF" cx="36" cy="36" r="21"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 410 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Filled / Folder / filled_warning</title>
|
||||
<g id="Icon-/-Filled-/-Folder-/-filled_warning" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M4.88979533,20 L19.1102047,20 C20.5643501,20 21.4708304,18.4289284 20.7437577,17.1777157 L13.633553,4.93840953 C12.9064803,3.68719682 11.0935197,3.68719682 10.366447,4.93840953 L3.25624234,17.1777157 C2.52916961,18.4289284 3.43564989,20 4.88979533,20 Z M12,9.51724138 C12.4659903,9.51724138 12.84375,9.89500112 12.84375,10.3609914 L12.84375,13.3631466 C12.84375,13.8291368 12.4659903,14.2068966 12,14.2068966 C11.5340097,14.2068966 11.15625,13.8291368 11.15625,13.3631466 L11.15625,10.3609914 C11.15625,9.89500112 11.5340097,9.51724138 12,9.51724138 Z M12.018319,17.1862069 L11.981681,17.1862069 C11.463676,17.1862069 11.04375,16.7662809 11.04375,16.2482759 C11.04375,15.7302709 11.463676,15.3103448 11.981681,15.3103448 L12.018319,15.3103448 C12.536324,15.3103448 12.95625,15.7302709 12.95625,16.2482759 C12.95625,16.7662809 12.536324,17.1862069 12.018319,17.1862069 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Filled / Folder / filled_rating_crown</title>
|
||||
<g id="Icon-/-Filled-/-Folder-/-filled_rating_crown" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M12.006194,4 C12.2804747,4 12.5305167,4.06540388 12.7563199,4.19621163 C12.9821231,4.32701939 13.1625206,4.50276893 13.2975123,4.72346025 C13.4325041,4.94415157 13.5,5.18550018 13.5,5.44750606 C13.5,5.71705917 13.4325041,5.96227898 13.2975123,6.18316549 C13.1625206,6.404052 12.9821231,6.5799154 12.7563199,6.71075569 L15.4435899,11.5643277 C15.5815617,11.7639089 15.7563694,11.7899721 15.968013,11.6425174 L18.1781768,9.33989673 C18.0593923,9.13003568 18,8.89851056 18,8.64532138 C18,8.38325043 18.0675127,8.14188556 18.202538,7.92122677 C18.3375634,7.70056798 18.5179776,7.52481844 18.7437808,7.39397816 C18.969584,7.26313787 19.2198274,7.19771773 19.494511,7.19771773 C19.7726861,7.19771773 20.0256488,7.26313787 20.2533991,7.39397816 C20.4811495,7.52481844 20.6625206,7.69962458 20.7975123,7.91839656 C20.9325041,8.13716855 21,8.37947682 21,8.64532138 C21,8.91526486 20.9315305,9.16058227 20.7945916,9.38127359 C20.6576527,9.60196491 20.4753248,9.77771445 20.247608,9.90852221 C20.0847544,10.0020705 19.9099761,10.0621678 19.7232729,10.0888143 L18.6881679,14.8280591 L18.18147,17.2645439 C18.0360821,17.8406994 17.5858498,18.2705453 17.2433245,18.5609529 C16.9007991,18.8513606 16.024907,19 15.4030968,19 L8.60301533,19 C7.98112362,19 7.07922209,18.853651 6.73645226,18.5609529 C6.39368244,18.2682549 5.96970407,17.8361186 5.82537564,17.2645439 L5.33200224,14.8280591 L4.28780473,10.091804 C4.09296054,10.0668449 3.91161924,10.005751 3.74378084,9.90852221 C3.51797764,9.77771445 3.33756337,9.60196491 3.20253802,9.38127359 C3.06751267,9.16058227 3,8.91526486 3,8.64532138 C3,8.38325043 3.06751267,8.14188556 3.20253802,7.92122677 C3.33756337,7.70056798 3.51797764,7.52481844 3.74378084,7.39397816 C3.96958405,7.26313787 4.21982744,7.19771773 4.49451103,7.19771773 C4.77268607,7.19771773 5.02564877,7.26313787 5.25339913,7.39397816 C5.48114949,7.52481844 5.66252056,7.69962458 5.79751234,7.91839656 C5.93250411,8.13716855 6,8.37947682 6,8.64532138 C6,8.85527742 5.9585802,9.05033621 5.8757406,9.23049773 L5.8356,9.31024634 L8.05741354,11.6413327 C8.17558193,11.7218918 8.27307085,11.7527925 8.3498803,11.7340349 C8.42668976,11.7152773 8.49488107,11.6628546 8.55445424,11.576767 L11.2500459,6.70934707 C11.0234694,6.57865415 10.8419403,6.40326029 10.7054588,6.18316549 C10.5684863,5.96227898 10.5,5.71705917 10.5,5.44750606 C10.5,5.18172656 10.5675127,4.93943456 10.702538,4.72063004 C10.8375634,4.50182552 11.0189344,4.32701939 11.2466512,4.19621163 C11.474368,4.06540388 11.7275489,4 12.006194,4 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Filled / Folder / filled_understood</title>
|
||||
<g id="Icon-/-Filled-/-Folder-/-filled_understood" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M12.8474094,2.35452236 C13.885383,1.47475174 14.8173541,2.39162557 15.6118925,3.37253311 L15.7012514,3.4837597 C16.5164257,4.50413739 16.896938,5.20519347 17.573079,6.45345067 C18.2023878,7.61607163 19,9.78891004 19,11.0676479 L18.9982623,11.4602681 C18.9876271,12.5429001 18.9217408,13.3886119 18.6297323,14.4132675 L18.4373147,15.0832437 C17.6686959,17.7193108 17.0159257,19.2763685 14.5216634,19.7807735 C13.10206,20.068163 10.6477559,20.1900858 9.65842399,19.3946847 C7.99649075,18.0581246 7.24548521,16.7868457 6.69332563,15.7108913 L6.48407363,15.2966137 C6.20747049,14.7436067 5.49035123,13.0903916 6.59969082,12.2935391 C7.07533115,11.9524455 7.88026093,12.0496935 8.21540442,12.3820783 C9.4579233,13.6143694 9.58232154,15.2464067 11.9078369,15.2464067 C13.3786631,15.2464067 13.9391869,14.2869904 13.9391869,12.58878 C13.9391869,11.3521345 12.1390712,10.1619357 9.97161486,11.0502304 C8.24621513,12.1140246 7.17909072,11.547078 6.86322591,10.7478144 L6.82781627,10.6508891 L6.79127892,10.5445502 C6.5448802,9.79724635 6.61229951,9.16312262 10.1911412,7.82363067 C10.6137438,7.6657143 11.3830789,7.698766 12.0161927,7.78792773 C12.8897042,8.04621996 13.6148836,8.35641256 14.1913276,8.71620571 C14.929645,9.17703372 15.7391454,9.82868756 16.6173642,10.6711738 C16.8017985,10.848104 17.0950516,10.8396614 17.2723632,10.6523169 C17.4496747,10.4649723 17.4439006,10.1696692 17.2594662,9.99273903 C16.7803672,9.5331334 16.3184045,9.12578598 15.8732309,8.77069768 L15.8624136,8.73672043 C15.3582916,7.35121785 14.8646161,6.25548747 14.3777651,5.44628421 C13.9445436,4.72621943 13.3206019,3.99276536 12.5068246,3.24305992 C12.4746432,2.90001775 12.5672164,2.59132364 12.8474094,2.35452236 Z M10.9668008,4.0469269 L11.0518595,3.98215031 C11.2745376,3.81947674 11.4718477,3.74284792 11.8308451,3.8850515 C12.6071746,4.59396408 13.1906226,5.27558261 13.5826599,5.9271944 C13.8835044,6.42723311 14.1925809,7.05721299 14.5092459,7.81609175 C13.7940229,7.39877614 12.9130339,7.05268239 11.8658275,6.77510676 L11.6720359,6.53222118 C11.6390059,6.49065589 11.6057339,6.44860798 11.5724054,6.40616676 L11.3960913,6.1777103 C10.8136406,5.40671311 10.3189783,4.5550951 10.9668008,4.0469269 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Mini / mini_gift_lock</title>
|
||||
<g id="Mini-/-mini_gift_lock" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M34.6666667,26.3333333 C40.1895142,26.3333333 44.6666667,30.8104858 44.6666667,36.3333333 C44.6666667,41.8561808 40.1895142,46.3333333 34.6666667,46.3333333 C29.1438192,46.3333333 24.6666667,41.8561808 24.6666667,36.3333333 C24.6666667,30.8104858 29.1438192,26.3333333 34.6666667,26.3333333 Z M22,3.06666667 C27.8542183,3.06666667 32.6,7.81244832 32.6,13.6666667 L32.5999548,19.4672746 C34.7232323,19.9235567 36.4292218,21.5034006 37.0650949,23.5539779 C36.287246,23.4090943 35.4858038,23.3333333 34.6666667,23.3333333 C27.4869649,23.3333333 21.6666667,29.1536316 21.6666667,36.3333333 C21.6666667,38.3649522 22.1326999,40.2877232 22.9636948,42.0005746 L12.6666667,42 C9.35295817,42 6.66666667,39.3137085 6.66666667,36 L6.66666667,25.3333333 C6.66666667,22.454169 8.69461762,20.048658 11.4000452,19.4672746 L11.4,13.6666667 C11.4,7.81244832 16.1457817,3.06666667 22,3.06666667 Z M34.6666667,29.3061633 C33.7052821,29.3061633 32.9259259,30.0855195 32.9259259,31.0469041 L32.9259259,36.9308449 C32.9259259,37.7182476 33.2996271,38.4589206 33.9329602,38.9267797 L37.3333333,41.3922119 C38.1066056,41.9634476 39.1965447,41.7996646 39.7677804,41.0263923 L39.8381314,40.9238035 C40.323622,40.1593232 40.1416125,39.1383445 39.4019608,38.5919452 L36.4074074,36.4264726 L36.4074074,31.0469041 C36.4074074,30.0855195 35.6280512,29.3061633 34.6666667,29.3061633 Z M22,7.6 C18.6494725,7.6 15.9333333,10.3161392 15.9333333,13.6666667 L15.9326667,19.3326667 L28.0666667,19.3326667 L28.0666667,13.6666667 C28.0666667,10.3161392 25.3505275,7.6 22,7.6 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / rating_gifts</title>
|
||||
<g id="Icon-/-Menu-/-rating_gifts" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M9.49180328,1 C11.2844653,1 12.6018066,2.18644541 12.6018066,3.65 C12.6018066,5.11355459 11.2844653,6.3 9.49180328,6.3 C7.6991413,6.3 6.24590164,5.11355459 6.24590164,3.65 C6.24590164,2.18644541 7.6991413,1 9.49180328,1 Z M9.49180328,2.2 C8.31430075,2.2 7.44590164,2.90897331 7.44590164,3.65 C7.44590164,4.39102669 8.31430075,5.1 9.49180328,5.1 C10.6309547,5.1 11.4018066,4.43203043 11.4018066,3.65 C11.4018066,2.86796957 10.6309547,2.2 9.49180328,2.2 Z" id="Oval" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M14.5081967,1 C16.3008587,1 17.7540984,2.18644541 17.7540984,3.65 C17.7540984,5.11355459 16.3008587,6.3 14.5081967,6.3 C12.7155347,6.3 11.4638672,5.11355459 11.4638672,3.65 C11.4638672,2.18644541 12.7155347,1 14.5081967,1 Z M14.5081967,2.2 C13.3883731,2.2 12.6638672,2.84700401 12.6638672,3.65 C12.6638672,4.45299599 13.3883731,5.1 14.5081967,5.1 C15.6856993,5.1 16.5540984,4.39102669 16.5540984,3.65 C16.5540984,2.90897331 15.6856993,2.2 14.5081967,2.2 Z" id="Oval" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M19.5245902,5.2245614 C20.6708074,5.2245614 21.6,6.15375404 21.6,7.29997124 L21.6,9.62036238 C21.6,10.2776718 21.0671455,10.8105263 20.4098361,10.8105263 L3.59016393,10.8105263 C2.93285454,10.8105263 2.4,10.2776718 2.4,9.62036238 L2.4,7.29997124 C2.4,6.15375404 3.32919263,5.2245614 4.47540984,5.2245614 L19.5245902,5.2245614 Z M19.5245902,6.4245614 L4.47540984,6.4245614 C3.99193433,6.4245614 3.6,6.81649574 3.6,7.29997124 L3.6,9.6095614 L20.4,9.6095614 L20.4,7.29997124 C20.4,6.85368616 20.0660441,6.48540066 19.6343997,6.43138209 L19.5245902,6.4245614 Z" id="Rectangle" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M5.07540984,10.7953216 L5.07540984,18.3761864 C5.07540984,19.3485697 5.86368226,20.1368421 6.83606557,20.1368421 L17.1639344,20.1368421 C18.1363177,20.1368421 18.9245902,19.3485697 18.9245902,18.3761864 L18.9245902,10.7953216 L20.1245902,10.7953216 L20.1245902,18.3761864 C20.1245902,20.0113114 18.7990594,21.3368421 17.1639344,21.3368421 L6.83606557,21.3368421 C5.20094056,21.3368421 3.87540984,20.0113114 3.87540984,18.3761864 L3.87540984,10.7953216 L5.07540984,10.7953216 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<polygon id="Path" fill="#FFFFFF" fill-rule="nonzero" points="11.4 6.11695906 11.4 21 12.6 21 12.6 6.11695906"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / rating_refund</title>
|
||||
<g id="Icon-/-Menu-/-rating_refund" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M9.58495703,2.34314741 C14.845204,0.914046898 20.2520851,4.07916838 21.661564,9.41264412 C23.0710429,14.7461199 19.9493787,20.2282711 14.6891317,21.6573716 C9.70832403,23.0105542 4.55852362,20.241996 2.84766326,15.3548697 L2.79687536,15.2061286 C2.7724639,15.132795 2.65678419,14.7396157 2.52176727,14.2756688 L2.02361181,12.5403036 C1.92527607,12.1682007 2.1430666,11.785725 2.51006058,11.6860203 C2.87705455,11.5863156 3.25427881,11.807138 3.35261455,12.179241 L3.59432993,13.0275538 C3.80113943,13.7451404 4.06828557,14.6632038 4.10050903,14.760005 C5.52633258,19.0432668 10.0041242,21.485941 14.3330265,20.3098676 C18.8592855,19.0801764 21.5453687,14.3629766 20.3325613,9.77370673 C19.1197538,5.18443691 14.4673212,2.46096029 9.94106224,3.69065143 C7.52370963,4.34739623 5.56035445,6.02376558 4.49357103,8.2653952 C4.32845825,8.61234632 3.91721015,8.75789206 3.57502268,8.59048072 C3.23283521,8.42306937 3.08928781,8.00609621 3.25440059,7.65914509 C4.49336656,5.05570865 6.77676829,3.10607433 9.58495703,2.34314741 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M2.49794681,3.90217796 C2.49794681,3.51694863 2.80594913,3.20465855 3.18588925,3.20465855 C3.56582936,3.20465855 3.87383169,3.51694863 3.87383169,3.90217796 L3.87360558,7.52173176 L6.91980245,7.41555441 C7.2814361,7.4029357 7.58746725,7.67568116 7.62708143,8.03426422 L7.63099908,8.08867052 C7.64406681,8.47367193 7.34684021,8.79651819 6.96712488,8.80976784 L3.24117729,8.93977968 C2.86111038,8.95304159 2.53984537,8.66451926 2.50172057,8.28653895 L2.49800979,8.22097241 L2.49794681,3.90217796 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M12.3540269,17.3251877 C12.6431336,17.3251877 12.9561718,17.1659567 12.9561718,16.7838024 L12.9561718,16.1901404 C14.6843872,16.0054325 15.7076642,14.9990928 15.7076642,13.5341679 C15.7076642,12.2730587 14.9431377,11.50875 13.3562633,11.1648111 L12.0520711,10.8718262 C11.2361478,10.6934875 10.8378231,10.3240717 10.8378231,9.78268638 C10.8378231,9.13302405 11.4031872,8.66170039 12.289781,8.66170039 C13.0093353,8.66170039 13.5104536,8.90373146 14.0693931,9.5215476 C14.3520752,9.81453257 14.5705113,9.92280963 14.8660426,9.92280963 C15.2258197,9.92280963 15.5020772,9.67440933 15.5020772,9.30499349 C15.5020772,8.94831613 15.2900657,8.55979258 14.9302885,8.20948446 C14.4548686,7.76363776 13.7920916,7.47065278 12.9890176,7.36874497 L12.9890176,6.69989325 C12.9890176,6.32410818 12.6816811,6.16487722 12.3861499,6.16487722 C12.0970432,6.16487722 11.7838443,6.31773894 11.7838443,6.69989325 L11.7838443,7.34963725 C10.1198749,7.50249898 9.11603231,8.48973095 9.11603231,9.90370191 C9.11603231,11.1393342 9.88055881,11.960966 11.3453659,12.2857971 L12.6495582,12.5851513 C13.6003978,12.8080747 13.992298,13.1392751 13.992298,13.6997681 C13.992298,14.4258613 13.4205093,14.8844465 12.3989991,14.8844465 C11.6344726,14.8844465 10.9984379,14.5978307 10.4202246,13.9800146 C10.0925704,13.6551834 9.91268177,13.5851218 9.66854726,13.5851218 C9.27664712,13.5851218 8.98111587,13.8335221 8.98111587,14.2602611 C8.98111587,14.6360462 9.19955201,15.0245697 9.59145215,15.3621394 C10.1054195,15.8270938 10.852439,16.1137095 11.7454573,16.1965096 L11.7454573,16.7838024 C11.7454573,17.1659567 12.0584957,17.3251877 12.3540269,17.3251877 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / users_stars</title>
|
||||
<g id="Icon-/-Menu-/-users_stars" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M10.6647222,14.6083333 C13.1679529,14.6083333 15.1972222,16.6376027 15.1972222,19.1408333 L15.1972222,19.1805556 C15.1972222,19.5119264 14.9285931,19.7805556 14.5972222,19.7805556 C14.2658514,19.7805556 13.9972222,19.5119264 13.9972222,19.1805556 L13.9972222,19.1408333 C13.9972222,17.3003444 12.5052112,15.8083333 10.6647222,15.8083333 L5.47983871,15.8083333 C3.75894714,15.8083333 2.36388889,17.2033916 2.36388889,18.9242832 C2.36388889,19.255654 2.09525974,19.5242832 1.76388889,19.5242832 C1.43251804,19.5242832 1.16388889,19.255654 1.16388889,18.9242832 C1.16388889,16.5406499 3.09620544,14.6083333 5.47983871,14.6083333 L10.6647222,14.6083333 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M8.47745116,3.81325275 C8.84434853,3.9691957 9.13648401,4.26133118 9.29242697,4.62822855 L9.837,5.91011111 L11.2536218,6.01848992 C12.0546381,6.07958882 12.6656371,6.74303885 12.6765549,7.53105369 L12.6721689,7.67131095 C12.640454,8.08709799 12.4414977,8.47225506 12.1207729,8.73875142 L11.07,9.61111111 L11.3938701,10.9206432 C11.5864903,11.7005519 11.1475494,12.4884712 10.4025244,12.745423 L10.2679383,12.785146 C9.87396987,12.8824474 9.45751925,12.8203294 9.10909531,12.6122923 L7.874,11.8751111 L6.65149995,12.6059661 C5.96413876,13.016376 5.08613461,12.8292111 4.62167189,12.196898 L4.53957882,12.0731599 C4.32863667,11.7198706 4.26783432,11.2968624 4.3707302,10.8984627 L4.60871396,9.97701978 C4.63955115,9.85762211 4.67934978,9.74174595 4.72741862,9.63019415 L4.781,9.51711111 C4.72402035,9.48693702 4.66830927,9.45442225 4.61377597,9.41969742 L4.45384681,9.30892937 L3.75085061,8.78062735 C3.07086182,8.2696154 2.93387902,7.30411853 3.44489097,6.62412974 C3.71113573,6.26984554 4.11709183,6.04742064 4.55898263,6.01371466 L5.912,5.91011111 L6.45757303,4.62822855 C6.79029762,3.84540525 7.69462787,3.48052816 8.47745116,3.81325275 Z M8.00805289,4.91763731 C7.83516435,4.84415422 7.63544068,4.92473827 7.5619576,5.09762682 L6.81242023,6.8611133 C6.762712,6.97806516 6.65195707,7.05755523 6.52524782,7.06722019 L4.65024959,7.21023893 C4.55265687,7.21768299 4.46300042,7.2668061 4.40419957,7.34505068 C4.29134126,7.49522796 4.32159427,7.70846043 4.47177155,7.82131874 L5.17476775,8.34962077 C5.44651754,8.55384088 5.78694643,8.64456615 6.12428682,8.60267031 L7.57841053,8.42207605 C7.70002725,8.40697192 7.81692252,8.4743167 7.86485999,8.58710305 C7.92415906,8.72662077 7.85912899,8.88779362 7.71961127,8.94709269 L6.4517079,9.48598834 C6.11405341,9.62950127 5.8623347,9.92186941 5.77058814,10.2771004 L5.53260437,11.1985433 C5.50987955,11.2865309 5.52330791,11.3799534 5.56989503,11.4579782 C5.66620013,11.6192716 5.87502492,11.671955 6.03631826,11.5756499 L7.70062419,10.5819253 C7.80803032,10.5177952 7.94196968,10.5177952 8.04937581,10.5819253 L9.72427701,11.5819761 C9.80122733,11.6279216 9.89320153,11.6416405 9.98021047,11.6201513 C10.1625874,11.5751082 10.2739185,11.3907479 10.2288754,11.208371 L9.77632428,9.37601536 C9.74506588,9.24945178 9.78889505,9.11615638 9.88916433,9.03284072 L11.3538674,7.81579097 C11.4247003,7.75693456 11.4686403,7.67187163 11.4756446,7.58004398 C11.4899322,7.3927312 11.3496676,7.2293018 11.1623548,7.21501419 L9.22475218,7.06722019 C9.09804293,7.05755523 8.987288,6.97806516 8.93757977,6.8611133 L8.1880424,5.09762682 C8.153602,5.01659659 8.08908313,4.95207771 8.00805289,4.91763731 Z" id="Star" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M18.0174227,5.90931057 C18.3486503,6.04652517 18.611812,6.3096869 18.7490266,6.64091449 L19.1044444,7.5 L20.0337984,7.57332883 C20.7344012,7.62815795 21.2683217,8.20775826 21.2801433,8.89635992 L21.2761548,9.02664858 C21.2481303,9.38474356 21.0787573,9.71696686 20.805417,9.94999576 L20.0974444,10.552 L20.3154363,11.4563406 C20.4799024,12.1395692 20.0937745,12.8265284 19.4425658,13.0506693 L19.3174274,13.0871582 C18.9686126,13.1711245 18.6007266,13.1129982 18.2947908,12.9255805 L17.4994444,12.438 L16.7339799,12.9079554 C16.1370798,13.2736191 15.3682309,13.1207349 14.9530722,12.5755767 L14.8749121,12.4613522 C14.6783232,12.1404454 14.6243898,11.7521195 14.7260855,11.389785 L14.9136874,10.721373 C14.9302657,10.6623057 14.9495081,10.6043097 14.9712932,10.5475329 L14.9924444,10.496 L14.8586084,10.4042198 L14.3173233,9.99771597 C13.7202762,9.54933497 13.5997585,8.701848 14.0481395,8.10480086 C14.2812853,7.79435268 14.6366446,7.59911993 15.0237072,7.56882844 L15.8944444,7.5 L16.2509734,6.64091449 C16.536738,5.95109613 17.3276044,5.6235459 18.0174227,5.90931057 Z M17.59643,6.9255611 C17.4678713,6.87230428 17.3204807,6.93334851 17.2672239,7.0619072 L16.7120856,8.40197687 C16.6758104,8.48954314 16.5934614,8.54934298 16.4989677,8.55673805 L15.1095308,8.66547529 C15.0373954,8.6711206 14.9711685,8.70750534 14.9277181,8.76536233 C14.8441551,8.87663162 14.8666155,9.03457438 14.9778848,9.11813736 L15.5191699,9.52464117 C15.7230236,9.67773481 15.9788884,9.7445585 16.2315688,9.71069694 L17.183292,9.58315701 C17.2805543,9.57012294 17.375435,9.61996727 17.4198915,9.70745139 C17.4776834,9.82117784 17.4323395,9.96022092 17.318613,10.0180128 L16.4184593,10.4754402 C16.2004755,10.5862121 16.0388382,10.7832041 15.9727639,11.0186219 L15.7851621,11.6870339 C15.7662094,11.7545607 15.7762608,11.8269315 15.8128983,11.8867376 C15.8855887,12.0053956 16.0407073,12.0426597 16.1593653,11.9699692 L17.3683823,11.2293199 C17.449152,11.17984 17.550848,11.17984 17.6316177,11.2293199 L18.8694054,11.9875943 C18.9264214,12.0225226 18.9949829,12.0333554 19.0599901,12.0177069 C19.1952788,11.9851403 19.2785516,11.8490667 19.245985,11.7137779 L18.9067247,10.304416 C18.8845188,10.212168 18.9160198,10.1152669 18.988225,10.0537103 L20.0917784,9.1129056 C20.1427197,9.06947694 20.1742851,9.00756181 20.1795079,8.94082508 C20.1903648,8.80209601 20.086704,8.68083261 19.9479749,8.66997568 L18.5010323,8.55673805 C18.4065386,8.54934298 18.3241896,8.48954314 18.2879144,8.40197687 L17.7327761,7.0619072 C17.707204,7.00017764 17.6581596,6.95113324 17.59643,6.9255611 Z" id="Star-Copy" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M18.2638889,14.6083333 C20.7890575,14.6083333 22.8361111,16.6553869 22.8361111,19.1805556 C22.8361111,19.5119264 22.567482,19.7805556 22.2361111,19.7805556 C21.9047403,19.7805556 21.6361111,19.5119264 21.6361111,19.1805556 C21.6361111,17.3181286 20.1263158,15.8083333 18.2638889,15.8083333 L15.8789648,15.8077138 C15.6342075,15.3537143 15.2908888,14.9468235 14.8660915,14.6068359 L18.2638889,14.6083333 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(9.000000, 14.000000)" stroke-width="7.2" stroke="rgb(255,255,255)">
|
||||
<path d="M2.96014341,0 L50.9898193,0 C51.9732032,-7.06402744e-15
|
||||
52.7703933,0.797190129 52.7703933,1.78057399 C52.7703933,2.08038611
|
||||
52.6946886,2.3753442 52.5502994,2.63809702 L29.699977,44.2200383
|
||||
C28.7527832,45.9436969 26.5876295,46.5731461 24.8639708,45.6259523
|
||||
C24.2556953,45.2916896 23.7583564,44.7869606 23.4331014,44.1738213
|
||||
L1.38718565,2.61498853 C0.926351231,1.74626794 1.25700829,0.668450654
|
||||
2.12572888,0.20761623 C2.38272962,0.0712838007 2.6692209,4.97530809e-16
|
||||
2.96014341,0 Z"></path>
|
||||
<line x1="27" y1="44.4532875" x2="27" y2="0"></line>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 835 B |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Filled / filled_verify_age</title>
|
||||
<g id="Filled-/-filled_verify_age" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M36,34.9473684 C33.5842105,34.9473684 31.5592105,34.1302632 29.925,32.4960526 C28.2907895,30.8618421 27.4736842,28.8368421 27.4736842,26.4210526 C27.4736842,24.0526316 28.2907895,22.0394737 29.925,20.3815789 C31.5592105,18.7236842 33.5842105,17.8947368 36,17.8947368 C38.3684211,17.8947368 40.3815789,18.7236842 42.0394737,20.3815789 C43.6973684,22.0394737 44.5263158,24.0526316 44.5263158,26.4210526 C44.5263158,28.8368421 43.6973684,30.8618421 42.0394737,32.4960526 C40.3815789,34.1302632 38.3684211,34.9473684 36,34.9473684 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M18.9473684,49.6842105 L18.9473684,47.1263158 C18.9473684,46.1315789 19.1960526,45.1960526 19.6934211,44.3197368 C20.1907895,43.4434211 20.8657895,42.7447368 21.7184211,42.2236842 C23.8973684,40.9447368 26.1828947,39.9736842 28.575,39.3105263 C30.9671053,38.6473684 33.4421053,38.3157895 36,38.3157895 C38.5578947,38.3157895 41.0328947,38.6473684 43.425,39.3105263 C45.8171053,39.9736842 48.1026316,40.9447368 50.2815789,42.2236842 C51.1342105,42.7447368 51.8092105,43.4434211 52.3065789,44.3197368 C52.8039474,45.1960526 53.0526316,46.1315789 53.0526316,47.1263158 L53.0526316,49.6842105 C53.0526316,50.4894737 52.7802632,51.1644737 52.2355263,51.7092105 C51.6907895,52.2539474 51.0157895,52.5263158 50.2105263,52.5263158 L21.7894737,52.5263158 C20.9842105,52.5263158 20.3092105,52.2539474 19.7644737,51.7092105 C19.2197368,51.1644737 18.9473684,50.4894737 18.9473684,49.6842105 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M11.6842105,66 C10.1210526,66 8.78289474,65.4434211 7.66973684,64.3302632 C6.55657895,63.2171053 6,61.8789474 6,60.3157895 L6,51.7894737 C6,50.9842105 6.27236842,50.3092105 6.81710526,49.7644737 C7.36184211,49.2197368 8.03684211,48.9473684 8.84210526,48.9473684 C9.64736842,48.9473684 10.3223684,49.2197368 10.8671053,49.7644737 C11.4118421,50.3092105 11.6842105,50.9842105 11.6842105,51.7894737 L11.6842105,60.3157895 L20.2105263,60.3157895 C21.0157895,60.3157895 21.6907895,60.5881579 22.2355263,61.1328947 C22.7802632,61.6776316 23.0526316,62.3526316 23.0526316,63.1578947 C23.0526316,63.9631579 22.7802632,64.6381579 22.2355263,65.1828947 C21.6907895,65.7276316 21.0157895,66 20.2105263,66 L11.6842105,66 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M6,20.2105263 L6,11.6842105 C6,10.1210526 6.55657895,8.78289474 7.66973684,7.66973684 C8.78289474,6.55657895 10.1210526,6 11.6842105,6 L20.2105263,6 C21.0157895,6 21.6907895,6.27236842 22.2355263,6.81710526 C22.7802632,7.36184211 23.0526316,8.03684211 23.0526316,8.84210526 C23.0526316,9.64736842 22.7802632,10.3223684 22.2355263,10.8671053 C21.6907895,11.4118421 21.0157895,11.6842105 20.2105263,11.6842105 L11.6842105,11.6842105 L11.6842105,20.2105263 C11.6842105,21.0157895 11.4118421,21.6907895 10.8671053,22.2355263 C10.3223684,22.7802632 9.64736842,23.0526316 8.84210526,23.0526316 C8.03684211,23.0526316 7.36184211,22.7802632 6.81710526,22.2355263 C6.27236842,21.6907895 6,21.0157895 6,20.2105263 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M60.3157895,66 L51.7894737,66 C50.9842105,66 50.3092105,65.7276316 49.7644737,65.1828947 C49.2197368,64.6381579 48.9473684,63.9631579 48.9473684,63.1578947 C48.9473684,62.3526316 49.2197368,61.6776316 49.7644737,61.1328947 C50.3092105,60.5881579 50.9842105,60.3157895 51.7894737,60.3157895 L60.3157895,60.3157895 L60.3157895,51.7894737 C60.3157895,50.9842105 60.5881579,50.3092105 61.1328947,49.7644737 C61.6776316,49.2197368 62.3526316,48.9473684 63.1578947,48.9473684 C63.9631579,48.9473684 64.6381579,49.2197368 65.1828947,49.7644737 C65.7276316,50.3092105 66,50.9842105 66,51.7894737 L66,60.3157895 C66,61.8789474 65.4434211,63.2171053 64.3302632,64.3302632 C63.2171053,65.4434211 61.8789474,66 60.3157895,66 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M60.3157895,20.2105263 L60.3157895,11.6842105 L51.7894737,11.6842105 C50.9842105,11.6842105 50.3092105,11.4118421 49.7644737,10.8671053 C49.2197368,10.3223684 48.9473684,9.64736842 48.9473684,8.84210526 C48.9473684,8.03684211 49.2197368,7.36184211 49.7644737,6.81710526 C50.3092105,6.27236842 50.9842105,6 51.7894737,6 L60.3157895,6 C61.8789474,6 63.2171053,6.55657895 64.3302632,7.66973684 C65.4434211,8.78289474 66,10.1210526 66,11.6842105 L66,20.2105263 C66,21.0157895 65.7276316,21.6907895 65.1828947,22.2355263 C64.6381579,22.7802632 63.9631579,23.0526316 63.1578947,23.0526316 C62.3526316,23.0526316 61.6776316,22.7802632 61.1328947,22.2355263 C60.5881579,21.6907895 60.3157895,21.0157895 60.3157895,20.2105263 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"cloud_lng_topup_purpose_subs" = "Buy **Stars** to keep your channel subscriptions.";
|
||||
|
||||
"cloud_lng_age_verify_about_gb#one" = "To access such content, you must confirm that you are at least **{count}** year old as required by UK law.";
|
||||
"cloud_lng_age_verify_about_gb#other" = "To access such content, you must confirm that you are at least **{count}** years old as required by UK law.";
|
||||
|
||||
"cloud_lng_passport_in_ar" = "Arabic";
|
||||
"cloud_lng_passport_in_az" = "Azerbaijani";
|
||||
"cloud_lng_passport_in_bg" = "Bulgarian";
|
||||
|
||||
@@ -377,6 +377,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
|
||||
"lng_intro_fragment_button" = "Open Fragment";
|
||||
|
||||
"lng_intro_email_setup_title" = "Choose a login email";
|
||||
"lng_intro_email_confirm_subtitle" = "Please check your email {email} (don't forget the spam folder) and enter the code we just sent you.";
|
||||
|
||||
"lng_phone_title" = "Your Phone Number";
|
||||
"lng_phone_desc" = "Please confirm your country code\nand enter your phone number.";
|
||||
"lng_phone_to_qr" = "Quick log in using QR code";
|
||||
@@ -512,6 +515,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_notify_global" = "Global settings";
|
||||
"lng_settings_notify_title" = "Notifications for chats";
|
||||
"lng_settings_desktop_notify" = "Desktop notifications";
|
||||
"lng_settings_master_volume_notifications" = "Volume";
|
||||
"lng_settings_native_title" = "System integration";
|
||||
"lng_settings_use_windows" = "Use Windows notifications";
|
||||
"lng_settings_skip_in_focus" = "Respect system Focus mode";
|
||||
@@ -552,12 +556,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_title_private_chats" = "Notifications for private chats";
|
||||
"lng_notification_about_private_chats#one" = "Please note that **{count} chat** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_private_chats#other" = "Please note that **{count} chats** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_private_chats" = "Notifications volume for private chats";
|
||||
"lng_notification_title_groups" = "Notifications for groups";
|
||||
"lng_notification_about_groups#one" = "Please note that **{count} group** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_groups#other" = "Please note that **{count} groups** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_groups" = "Notifications volume for groups";
|
||||
"lng_notification_title_channels" = "Notifications for channels";
|
||||
"lng_notification_about_channels#one" = "Please note that **{count} channel** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_channels#other" = "Please note that **{count} channels** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_channel" = "Notifications volume for channels";
|
||||
"lng_notification_exceptions_view" = "View exceptions";
|
||||
"lng_notification_enable" = "Enable notifications";
|
||||
"lng_notification_sound" = "Sound";
|
||||
@@ -687,6 +694,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
"lng_shortcuts_show_chat_preview" = "Show chat preview";
|
||||
"lng_shortcuts_record_voice_message" = "Record Voice Message";
|
||||
"lng_shortcuts_record_round_message" = "Record Round Message";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
@@ -1238,6 +1247,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_quick_dialog_action_toast_archive_success" = "The chat has been archived.";
|
||||
"lng_quick_dialog_action_toast_unarchive_success" = "The chat has been unarchived.";
|
||||
|
||||
"lng_archive_hint_title" = "This is your Archive";
|
||||
"lng_archive_hint_about" = "Archived chats will remain in the Archive when you receive a new message. {link}";
|
||||
"lng_archive_hint_about_unmuted" = "When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. {link}";
|
||||
"lng_archive_hint_about_link" = "Tap to change {emoji}";
|
||||
"lng_archive_hint_section_1" = "Archived Chats";
|
||||
"lng_archive_hint_section_1_info" = "Move any chat into your Archive and back by swiping on it.";
|
||||
"lng_archive_hint_section_2" = "Hiding Archive";
|
||||
"lng_archive_hint_section_2_info" = "Hide the Archive from your Main screen by swiping on it.";
|
||||
"lng_archive_hint_section_3" = "Stories";
|
||||
"lng_archive_hint_section_3_info" = "Archive Stories from your contacts separately from chats with them.";
|
||||
"lng_archive_hint_button" = "Got it";
|
||||
|
||||
"lng_settings_generic_subscribe" = "Subscribe to {link} to use this setting.";
|
||||
"lng_settings_generic_subscribe_link" = "Telegram Premium";
|
||||
|
||||
@@ -1638,6 +1659,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_type_audios" = "Voice messages";
|
||||
"lng_media_type_links" = "Shared links";
|
||||
"lng_media_type_rounds" = "Video messages";
|
||||
"lng_media_saved_music_your" = "Your playlist";
|
||||
"lng_media_saved_music_title" = "Playlist";
|
||||
"lng_profile_common_groups_section" = "Groups in common";
|
||||
"lng_info_edit_contact" = "Edit contact";
|
||||
"lng_info_delete_contact" = "Delete contact";
|
||||
@@ -1876,6 +1899,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_star_ref_revoked_title" = "Link removed";
|
||||
"lng_star_ref_revoked_text" = "It will no longer work.";
|
||||
|
||||
"lng_stars_rating_title" = "Rating";
|
||||
"lng_stars_rating_future" = "Future Rating";
|
||||
"lng_stars_rating_updates#one" = "in {count} day";
|
||||
"lng_stars_rating_updates#other" = "in {count} days";
|
||||
"lng_stars_rating_pending#one" = "The rating will update {when}.\n{count} point is pending. {link}";
|
||||
"lng_stars_rating_pending#other" = "The rating will update {when}.\n{count} points are pending. {link}";
|
||||
"lng_stars_rating_pending_preview" = "Preview {arrow}";
|
||||
"lng_stars_rating_pending_back" = "Back {arrow}";
|
||||
"lng_stars_rating_negative_label" = "Negative Rating";
|
||||
"lng_stars_rating_negative" = "A negative rating indicates that **{name}'s** payments are unreliable.";
|
||||
"lng_stars_rating_negative_your#one" = "A negative rating indicates that your payments are unreliable. Spend **{count} Star** to fix this issue.";
|
||||
"lng_stars_rating_negative_your#other" = "A negative rating indicates that your payments are unreliable. Spend **{count} Stars** to fix this issue.";
|
||||
"lng_stars_rating_about" = "This rating reflects **{name}'s** activity on Telegram. What affects it:";
|
||||
"lng_stars_rating_about_your" = "This rating reflects your activity on Telegram. What affects it:";
|
||||
"lng_stars_title_gifts_telegram" = "Gifts from Telegram";
|
||||
"lng_stars_about_gifts_telegram" = "{emoji} 100% of the Stars spent on gifts purchased from Telegram.";
|
||||
"lng_stars_title_gifts_users" = "Gifts and Posts from Users";
|
||||
"lng_stars_about_gifts_users" = "{emoji} 20% of the Stars spent on resold gifts, paid messages and channel posts.";
|
||||
"lng_stars_title_refunds" = "Refunds and Conversions";
|
||||
"lng_stars_about_refunds" = "{emoji} 10x of refunded Stars and 85% of bought gifts converted to Stars.";
|
||||
"lng_stars_rating_added" = "Added";
|
||||
"lng_stars_rating_deducted" = "Deducted";
|
||||
"lng_stars_rating_understood" = "Understood";
|
||||
|
||||
"lng_manage_discussion_group" = "Discussion";
|
||||
"lng_manage_discussion_group_add" = "Add a group";
|
||||
"lng_manage_linked_channel" = "Linked channel";
|
||||
@@ -1903,6 +1950,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_monoforum_price" = "Price for each message";
|
||||
"lng_manage_monoforum_about" = "Allow users to send messages to your channel, with the option to charge a fee for each message.";
|
||||
"lng_manage_monoforum_price_about" = "Your channel will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||
"lng_manage_monoforum_link_subtitle" = "Link to direct messages";
|
||||
|
||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||
"lng_manage_history_visibility_shown" = "Visible";
|
||||
@@ -2031,6 +2079,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
|
||||
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_group_history" = "Are you sure you want to delete all messages in \"{group}\"?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_channel_history" = "Are you sure you want to delete all messages in \"{channel}\"?\n\n**This action cannot be undone.**";
|
||||
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
|
||||
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
|
||||
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.";
|
||||
@@ -2143,6 +2192,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
|
||||
"lng_action_you_theme_changed" = "You changed the chat theme to {emoji}";
|
||||
"lng_action_theme_changed" = "{from} changed the chat theme to {emoji}";
|
||||
"lng_action_you_gift_theme_changed" = "You set {name} as a new theme for this chat.";
|
||||
"lng_action_gift_theme_changed" = "{from} set {name} as a new theme for this chat.";
|
||||
"lng_action_you_theme_disabled" = "You disabled the chat theme";
|
||||
"lng_action_theme_disabled" = "{from} disabled the chat theme";
|
||||
"lng_action_proximity_distance_m#one" = "{count} meter";
|
||||
@@ -2159,6 +2210,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_upgraded_self_channel" = "You turned this gift to {channel} into a unique collectible";
|
||||
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible";
|
||||
"lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible";
|
||||
"lng_action_gift_sent_upgrade_other" = "{from} sent an upgrade worth {cost} for the gift you received from {user}.";
|
||||
"lng_action_gift_sent_upgrade_self_other" = "You sent an upgrade worth {cost} for the gift {name} received from {user}.";
|
||||
"lng_action_gift_sent_upgrade" = "{from} sent an upgrade worth {cost} for your gift.";
|
||||
"lng_action_gift_sent_upgrade_self" = "You sent an upgrade worth {cost} for this gift.";
|
||||
"lng_action_gift_sent_upgrade_self_channel" = "You sent an upgrade worth {cost} for your gift to {name}.";
|
||||
"lng_action_gift_upgraded_helped" = "{user} unpacked the gift that you helped to upgrade.";
|
||||
"lng_action_gift_upgraded_helped_self" = "You unpacked the gift that {user} helped to upgrade.";
|
||||
"lng_action_gift_transferred" = "{user} transferred you a gift";
|
||||
"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_unknown" = "Someone transferred you a gift";
|
||||
@@ -2179,6 +2237,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_channel_about_unique" = "You can display this gift in channel's Gifts or turn it into unique collectible.";
|
||||
"lng_action_gift_for_stars#one" = "{count} Star";
|
||||
"lng_action_gift_for_stars#other" = "{count} Stars";
|
||||
"lng_action_gift_for_ton#one" = "{count} TON";
|
||||
"lng_action_gift_for_ton#other" = "{count} TON";
|
||||
"lng_action_gift_got_subtitle" = "Gift from {user}";
|
||||
"lng_action_gift_got_stars_text#one" = "Display this gift on your page or convert it to **{count}** Star.";
|
||||
"lng_action_gift_got_stars_text#other" = "Display this gift on your page or convert it to **{count}** Stars.";
|
||||
@@ -2313,6 +2373,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_peer_gifts_filter_by_value" = "Sort by Value";
|
||||
"lng_peer_gifts_filter_by_date" = "Sort by Date";
|
||||
"lng_peer_gifts_filter_unlimited" = "Unlimited";
|
||||
"lng_peer_gifts_filter_upgradable" = "Upgradeable";
|
||||
"lng_peer_gifts_filter_limited" = "Limited";
|
||||
"lng_peer_gifts_filter_unique" = "Unique";
|
||||
"lng_peer_gifts_filter_saved" = "Displayed";
|
||||
@@ -2589,6 +2650,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_premium_unlock_emoji" = "Unlock Animated Emoji";
|
||||
"lng_premium_unlock_status" = "Unlock Emoji Status";
|
||||
|
||||
"lng_premium_subscribe_months_24" = "2-Year";
|
||||
"lng_premium_subscribe_months_12" = "Annual";
|
||||
"lng_premium_subscribe_months_6" = "Semiannual";
|
||||
"lng_premium_subscribe_months_1" = "Monthly";
|
||||
@@ -2875,6 +2937,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
||||
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
|
||||
"lng_credits_box_history_entry_currency_in" = "TON Top-Up";
|
||||
"lng_credits_box_history_entry_posts_search" = "Posts Search";
|
||||
"lng_credits_box_history_entry_api" = "Paid Broadcast";
|
||||
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
|
||||
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
|
||||
@@ -2939,6 +3002,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
|
||||
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
|
||||
"lng_credits_small_balance_for_suggest" = "Buy **Stars** to suggest post to {channel}.";
|
||||
"lng_credits_small_balance_for_search" = "Buy **Stars** to search through public posts.";
|
||||
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
|
||||
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
||||
"lng_credits_enough" = "You have enough stars at the moment. {link}";
|
||||
@@ -3508,6 +3572,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_ton_amount#one" = "{count} TON";
|
||||
"lng_gift_ton_amount#other" = "{count} TON";
|
||||
|
||||
"lng_gift_premium_title" = "Premium Gift";
|
||||
"lng_gift_premium_text#one" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock access to multiple additional features.";
|
||||
"lng_gift_premium_text#other" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock access to multiple additional features.";
|
||||
"lng_gift_premium_or_stars" = "Gift Premium or Stars";
|
||||
"lng_gift_premium_subtitle" = "Gift Premium";
|
||||
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
|
||||
@@ -3521,6 +3588,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_stars_sold_out" = "sold out";
|
||||
"lng_gift_stars_resale" = "resale";
|
||||
"lng_gift_stars_on_sale" = "on sale";
|
||||
"lng_gift_stars_premium" = "premium";
|
||||
"lng_gift_stars_tabs_all" = "All Gifts";
|
||||
"lng_gift_stars_tabs_my" = "My Gifts";
|
||||
"lng_gift_stars_tabs_limited" = "Limited";
|
||||
@@ -3549,6 +3617,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_send_button_self" = "Buy a Gift for {cost}";
|
||||
"lng_gift_buy_resale_title" = "Buy {name}";
|
||||
"lng_gift_buy_resale_button" = "Buy for {cost}";
|
||||
"lng_gift_buy_resale_equals" = "Equals to {cost}";
|
||||
"lng_gift_buy_resale_only_ton" = "The seller only accepts TON as payment.";
|
||||
"lng_gift_buy_resale_pay_stars" = "Pay in Stars";
|
||||
"lng_gift_buy_resale_pay_ton" = "Pay in TON";
|
||||
"lng_gift_buy_resale_confirm" = "Do you want to buy {name} for {price} and gift it to {user}?";
|
||||
"lng_gift_buy_resale_confirm_self" = "Do you want to buy {name} for {price}?";
|
||||
"lng_gift_buy_price_change_title" = "Price change!";
|
||||
@@ -3558,6 +3630,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_sent_resale_done_self" = "{gift} is now yours.";
|
||||
"lng_gift_sent_about#one" = "You spent **{count}** Star from your balance.";
|
||||
"lng_gift_sent_about#other" = "You spent **{count}** Stars from your balance.";
|
||||
"lng_gift_sent_finished#one" = "You've already sent **{count}** of these gifts, and it's the limit.";
|
||||
"lng_gift_sent_finished#other" = "You've already sent **{count}** of these gifts, and it's the limit.";
|
||||
"lng_gift_sent_remains#one" = "You can send **{count}** more.";
|
||||
"lng_gift_sent_remains#other" = "You can send **{count}** more.";
|
||||
"lng_gift_limited_of_one" = "unique";
|
||||
"lng_gift_limited_of_count" = "1 of {amount}";
|
||||
"lng_gift_collectible_tag" = "gift";
|
||||
@@ -3570,8 +3646,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_hidden_hint_channel" = "This gift is hidden from visitors of your channel.";
|
||||
"lng_gift_visible_hint_channel" = "This gift is visible in your channel's Gifts.";
|
||||
"lng_gift_in_blockchain" = "This gift is in TON blockchain. {link}";
|
||||
"lng_gift_in_blockchain_link" = "View >";
|
||||
"lng_gift_visible_hide" = "Hide >";
|
||||
"lng_gift_in_blockchain_link_arrow" = "View {arrow}";
|
||||
"lng_gift_visible_hide_arrow" = "Hide {arrow}";
|
||||
"lng_gift_visible_show_arrow" = "Show {arrow}";
|
||||
"lng_gift_show_on_page" = "Display on my Page";
|
||||
"lng_gift_show_on_channel" = "Display in channel's Gifts";
|
||||
"lng_gift_availability" = "Availability";
|
||||
@@ -3590,7 +3667,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
||||
"lng_gift_unique_status" = "Status";
|
||||
"lng_gift_unique_status_non" = "Non-Unique";
|
||||
"lng_gift_unique_status_upgrade" = "upgrade";
|
||||
"lng_gift_unique_upgrade" = "Upgrade";
|
||||
"lng_gift_unique_upgrade_next" = "Upgrade Next Gift";
|
||||
"lng_gift_unique_gift_upgrade" = "Gift an Upgrade";
|
||||
"lng_gift_unique_number" = "Collectible #{index}";
|
||||
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
|
||||
"lng_gift_unique_model" = "Model";
|
||||
@@ -3600,6 +3679,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_availability_label" = "Quantity";
|
||||
"lng_gift_unique_availability#one" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_availability#other" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_value" = "Value";
|
||||
"lng_gift_unique_value_learn_more" = "learn more";
|
||||
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender" = "Gifted by {from} to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\".";
|
||||
@@ -3608,6 +3689,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_availability_left#one" = "{count} of {amount} left";
|
||||
"lng_gift_availability_left#other" = "{count} of {amount} left";
|
||||
"lng_gift_availability_none" = "None of {amount} left";
|
||||
"lng_gift_value_about_average" = "This is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_about_last" = "This is the price at which {gift} was last sold on {platform}.";
|
||||
"lng_gift_value_initial_sale" = "Initial Sale";
|
||||
"lng_gift_value_initial_price" = "Initial Price";
|
||||
"lng_gift_value_initial_price_value" = "{stars} ({amount})";
|
||||
"lng_gift_value_last_sale" = "Last Sale";
|
||||
"lng_gift_value_last_price" = "Last Price";
|
||||
"lng_gift_value_minimum_price" = "Minimum Price";
|
||||
"lng_gift_value_minimum_price_tooltip" = "{amount} is the floor price for {gift} gifts listed on Telegram and Fragment.";
|
||||
"lng_gift_vlaue_average_price" = "Average Price";
|
||||
"lng_gift_value_average_price_tooltip" = "{amount} is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_availability#one" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_availability#other" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_telegram" = "Telegram";
|
||||
"lng_gift_value_fragment" = "Fragment";
|
||||
"lng_gift_convert_to_stars#one" = "Convert to {count} Star";
|
||||
"lng_gift_convert_to_stars#other" = "Convert to {count} Stars";
|
||||
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
|
||||
@@ -3643,10 +3739,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_preview_about_channel" = "Let the admins of {name} turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_unique_title" = "Unique";
|
||||
"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_user" = "{name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_channel" = "Admins of {name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_transferable_title" = "Transferable";
|
||||
"lng_gift_upgrade_transferable_about" = "Send your upgraded gift to any of your friends on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_user" = "{name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_channel" = "Admins of {name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_tradable_title" = "Tradable";
|
||||
"lng_gift_upgrade_tradable_about" = "Sell or auction your gift on third-party NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_user" = "{name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_channel" = "Admins of {name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_button" = "Upgrade for {price}";
|
||||
"lng_gift_upgrade_free" = "Upgrade for Free";
|
||||
"lng_gift_upgrade_confirm" = "Confirm";
|
||||
@@ -3656,6 +3758,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_add_comment" = "Add sender's name and comment";
|
||||
"lng_gift_upgraded_title" = "Gift Upgraded";
|
||||
"lng_gift_upgraded_about" = "Your gift {name} now has unique attributes and can be transferred to others";
|
||||
"lng_gift_upgrade_gifted_title" = "Upgrade Gifted";
|
||||
"lng_gift_upgrade_gifted_about" = "Now {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_gifted_about_channel" = "Now the admins of {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_transferred_title" = "Gift Transferred";
|
||||
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
|
||||
"lng_gift_transfer_title" = "Transfer {name}";
|
||||
@@ -3682,6 +3787,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?";
|
||||
"lng_gift_transfer_button" = "Transfer";
|
||||
"lng_gift_transfer_button_for" = "Transfer for {price}";
|
||||
"lng_gift_transfer_set_theme" = "Set as Theme in...";
|
||||
"lng_gift_transfer_choose" = "Choose Chat";
|
||||
"lng_gift_transfer_wear" = "Wear";
|
||||
"lng_gift_transfer_take_off" = "Take Off";
|
||||
"lng_gift_transfer_sell" = "Sell";
|
||||
@@ -3690,12 +3797,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_sell_unlist_title" = "Unlist {name}";
|
||||
"lng_gift_sell_unlist_sure" = "Are you sure you want to unlist your gift?";
|
||||
"lng_gift_sell_title" = "Price in Stars";
|
||||
"lng_gift_sell_placeholder" = "Enter price in Stars";
|
||||
"lng_gift_sell_about" = "You will receive {percent} of the selected amount.";
|
||||
"lng_gift_sell_amount#one" = "You will receive **{count}** Star.";
|
||||
"lng_gift_sell_amount#other" = "You will receive **{count}** Stars.";
|
||||
"lng_gift_sell_min_price#one" = "Minimum price is {count} Star.";
|
||||
"lng_gift_sell_min_price#other" = "Minimum price is {count} Stars.";
|
||||
"lng_gift_sell_only_ton" = "Only Accept TON";
|
||||
"lng_gift_sell_only_ton_about" = "If the buyer pays you in TON, there's no risk of refunds, unlike Stars payments.";
|
||||
"lng_gift_sell_amount_ton#one" = "You will receive **{count}** TON.";
|
||||
"lng_gift_sell_amount_ton#other" = "You will receive **{count}** TON.";
|
||||
"lng_gift_sell_min_price_ton#one" = "Minimum price is {count} TON.";
|
||||
"lng_gift_sell_min_price_ton#other" = "Minimum price is {count} TON.";
|
||||
"lng_gift_sell_title_ton" = "Price in TON";
|
||||
"lng_gift_sell_put" = "Put for Sale";
|
||||
"lng_gift_sell_update" = "Update the Price";
|
||||
"lng_gift_sell_toast" = "{name} is now for sale!";
|
||||
@@ -3735,9 +3848,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_resale_symbol" = "Symbol";
|
||||
"lng_gift_resale_symbols#one" = "{count} Symbol";
|
||||
"lng_gift_resale_symbols#other" = "{count} Symbols";
|
||||
"lng_gift_resale_switch_to_ton" = "Switch to Ton";
|
||||
"lng_gift_resale_switch_to_stars" = "Switch to Stars";
|
||||
"lng_gift_resale_early" = "You will be able to resell this gift in {duration}.";
|
||||
"lng_gift_transfer_early" = "You will be able to transfer this gift in {duration}.";
|
||||
"lng_gift_resale_transfer_early_title" = "Try Later";
|
||||
"lng_gift_collection_add" = "Add Collection";
|
||||
"lng_gift_collection_new_title" = "Create a New Collection";
|
||||
"lng_gift_collection_new_button" = "New Collection";
|
||||
"lng_gift_collection_new_text" = "Choose a name for your collection and start adding your gifts there.";
|
||||
"lng_gift_collection_new_ph" = "Title";
|
||||
"lng_gift_collection_new_create" = "Create";
|
||||
"lng_gift_collection_empty_title" = "Organize Your Gifts";
|
||||
"lng_gift_collection_empty_text" = "Add some of your gifts to this collection.";
|
||||
"lng_gift_collection_all" = "All Gifts";
|
||||
"lng_gift_collection_add_title" = "Add Gifts";
|
||||
"lng_gift_collection_add_button" = "Add Gifts";
|
||||
"lng_gift_collection_share" = "Share Collection";
|
||||
"lng_gift_collection_edit" = "Edit Name";
|
||||
"lng_gift_collection_limit_title" = "Limit Reached";
|
||||
"lng_gift_collection_limit_text" = "Please remove one of the existing collections to add a new one.";
|
||||
"lng_gift_collection_delete" = "Delete Collection";
|
||||
"lng_gift_collection_delete_sure" = "Are you sure you want to delete this collection?";
|
||||
"lng_gift_collection_delete_button" = "Delete";
|
||||
"lng_gift_collection_add_to" = "Add to Collection";
|
||||
"lng_gift_locked_title" = "Gift Locked";
|
||||
|
||||
"lng_accounts_limit_title" = "Limit Reached";
|
||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
|
||||
@@ -4056,6 +4191,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dialogs_suggestions_credits_sub_low_title#other" = "{emoji} {count} Stars needed for {channels}";
|
||||
"lng_dialogs_suggestions_credits_sub_low_about" = "Insufficient funds to cover your subscription.";
|
||||
|
||||
"lng_unconfirmed_auth_title" = "Someone just got access to your messages!";
|
||||
"lng_unconfirmed_auth_confirm" = "Yes, it’s me";
|
||||
"lng_unconfirmed_auth_deny" = "No, it’s not me!";
|
||||
"lng_unconfirmed_auth_single" = "We detected a new login to your account from {from}, {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#one" = "We detected new {count} login to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#other" = "We detected new {count} logins to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#one" = "We detected new {count} login to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#other" = "We detected new {count} logins to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_denied_title#one" = "New Login Prevented";
|
||||
"lng_unconfirmed_auth_denied_title#other" = "New Logins Prevented";
|
||||
"lng_unconfirmed_auth_denied_single" = "We have terminated the login attempt from {country}.";
|
||||
"lng_unconfirmed_auth_denied_multiple" = "We have terminated the login attempts from: {country}";
|
||||
"lng_unconfirmed_auth_denied_warning" = "Never send your login code to anyone or you can lose your Telegram account!";
|
||||
"lng_unconfirmed_auth_confirmed" = "New Login Allowed";
|
||||
"lng_unconfirmed_auth_confirmed_message" = "You can check the list of your active logins in {link}.";
|
||||
|
||||
"lng_about_random" = "Send a {emoji} emoji to any chat to try your luck.";
|
||||
"lng_about_random_send" = "Send";
|
||||
|
||||
@@ -4223,6 +4374,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_archive_to_list" = "Move to chat list";
|
||||
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nRight click the archive button to return the Archive to your chat list.";
|
||||
"lng_context_archive_settings" = "Archive settings";
|
||||
"lng_context_archive_how_does_it_work" = "How does it work?";
|
||||
|
||||
"lng_context_mute" = "Mute notifications";
|
||||
"lng_context_unmute" = "Unmute";
|
||||
@@ -4234,6 +4386,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_remove_from_group" = "Remove from group";
|
||||
"lng_context_add_to_group" = "Add to group";
|
||||
|
||||
"lng_context_rate_transcription" = "Rate transcription";
|
||||
"lng_toast_sent_rate_transcription" = "Thank you for your feedback!";
|
||||
|
||||
"lng_context_copy_link" = "Copy Link";
|
||||
"lng_context_copy_message_link" = "Copy Message Link";
|
||||
"lng_context_copy_post_link" = "Copy Post Link";
|
||||
@@ -4253,6 +4408,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_pack_info" = "View Sticker Set";
|
||||
"lng_context_pack_add" = "Add Stickers";
|
||||
"lng_context_save_file" = "Save As...";
|
||||
"lng_context_save_music_to" = "Save to...";
|
||||
"lng_context_save_music_profile" = "... Profile";
|
||||
"lng_context_save_music_saved" = "... Saved Messages";
|
||||
"lng_context_save_music_folder" = "... Downloads";
|
||||
"lng_context_save_music_about" = "Choose where you want this audio to be saved.";
|
||||
"lng_context_copy_text" = "Copy Text";
|
||||
"lng_context_open_gif" = "Open GIF";
|
||||
"lng_context_save_gif" = "Add to GIFs";
|
||||
@@ -4667,6 +4827,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
"lng_selected_delete_sure#one" = "Do you want to delete {count} message?";
|
||||
"lng_selected_delete_sure#other" = "Do you want to delete {count} messages?";
|
||||
"lng_selected_remove_saved_music" = "Do you want to remove this file from your profile?";
|
||||
"lng_saved_music_added" = "Audio added to your Profile.";
|
||||
"lng_saved_music_removed" = "Audio removed from your Profile.";
|
||||
"lng_delete_photo_sure" = "Do you want to delete this photo?";
|
||||
"lng_delete_for_everyone_hint#one" = "This will delete it for everyone in this chat.";
|
||||
"lng_delete_for_everyone_hint#other" = "This will delete them for everyone in this chat.";
|
||||
@@ -6192,6 +6355,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_chat_theme_change_wallpaper" = "Change Wallpaper";
|
||||
"lng_chat_theme_title" = "Select theme";
|
||||
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you have an unsent voice message.";
|
||||
"lng_chat_theme_gift_replace" = "This gift is already your theme in the chat with {name}. Remove it there and use it here instead?";
|
||||
|
||||
"lng_photo_editor_menu_delete" = "Delete";
|
||||
"lng_photo_editor_menu_flip" = "Flip";
|
||||
@@ -6224,10 +6388,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_view_button_emojipack" = "View emoji";
|
||||
"lng_view_button_collectible" = "View collectible";
|
||||
"lng_view_button_call" = "Join call";
|
||||
"lng_view_button_storyalbum" = "View Album";
|
||||
"lng_view_button_collection" = "View Collection";
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_linked" = "Unlike other apps, Telegram never uses your private data to target ads. {link}\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_link" = "Learn more in the Privacy Policy";
|
||||
"lng_sponsored_info_description1_url" = "https://telegram.org/privacy#5-6-no-ads-based-on-user-data";
|
||||
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together.";
|
||||
"lng_sponsored_info_menu" = "Advertiser info";
|
||||
"lng_sponsored_info_submenu" = "Advertiser: {text}";
|
||||
@@ -6264,6 +6432,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_ringtones_box_title" = "Notification Sound";
|
||||
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
||||
"lng_ringtones_box_volume" = "Volume";
|
||||
"lng_ringtones_box_upload_choose" = "Choose a tone";
|
||||
"lng_ringtones_box_upload_button" = "Upload Sound";
|
||||
"lng_ringtones_box_about" = "Right click on any short voice note or MP3 file in chat and select \"Save for Notifications\". It will appear here.";
|
||||
@@ -6388,6 +6557,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stories_archive_title" = "Story Archive";
|
||||
"lng_stories_archive_about" = "Only you can see archived stories unless you choose to post them to your profile.";
|
||||
"lng_stories_channel_archive_about" = "Only admins of the channel can see archived stories unless they are posted to the channel page.";
|
||||
"lng_stories_empty" = "Your stories will be here.";
|
||||
"lng_stories_empty_channel" = "Channel posts will be here.";
|
||||
"lng_stories_reply_sent" = "Message sent.";
|
||||
"lng_stories_hidden_to_contacts" = "Stories from {user} were moved to the **Archive**.";
|
||||
"lng_stories_shown_in_chats" = "Stories from {user} were returned from the **Archive**.";
|
||||
@@ -6423,6 +6594,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stories_save_promo" = "Subscribe to {link} to save other people's stories that are not protected.";
|
||||
"lng_stories_reaction_as_message" = "Send reaction as a private message";
|
||||
|
||||
"lng_stories_album_add" = "Add Album";
|
||||
"lng_stories_album_new_title" = "Create a New Album";
|
||||
"lng_stories_album_new_button" = "New Album";
|
||||
"lng_stories_album_new_text" = "Choose a name for your album and start adding your stories there.";
|
||||
"lng_stories_album_new_ph" = "Title";
|
||||
"lng_stories_album_new_create" = "Create";
|
||||
"lng_stories_album_empty_title" = "Organize Your Stories";
|
||||
"lng_stories_album_empty_text" = "Add some of your stories to this album.";
|
||||
"lng_stories_album_all" = "All Stories";
|
||||
"lng_stories_album_add_title" = "Add Stories";
|
||||
"lng_stories_album_add_button" = "Add Stories";
|
||||
"lng_stories_album_share" = "Share Album";
|
||||
"lng_stories_album_edit" = "Edit Name";
|
||||
"lng_stories_album_limit_title" = "Limit Reached";
|
||||
"lng_stories_album_limit_text" = "Please remove one of the existing albums to add a new one.";
|
||||
"lng_stories_album_delete" = "Delete Album";
|
||||
"lng_stories_album_delete_sure" = "Are you sure you want to delete this album?";
|
||||
"lng_stories_album_delete_button" = "Delete";
|
||||
"lng_stories_album_add_to" = "Add to Album";
|
||||
|
||||
"lng_stealth_mode_menu_item" = "Hide My View";
|
||||
"lng_stealth_mode_title" = "Stealth Mode";
|
||||
"lng_stealth_mode_unlock_about" = "Subscribe to Telegram Premium to watch stories without appearing in the list of viewers.";
|
||||
@@ -6679,6 +6870,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_recent_chats" = "Chats";
|
||||
"lng_recent_channels" = "Channels";
|
||||
"lng_recent_apps" = "Apps";
|
||||
"lng_recent_posts" = "Posts";
|
||||
"lng_all_photos" = "Photos";
|
||||
"lng_all_videos" = "Videos";
|
||||
"lng_all_downloads" = "Downloads";
|
||||
@@ -6696,6 +6888,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_bot_apps_popular" = "Grossing apps";
|
||||
"lng_bot_apps_which" = "Which apps are included here? {link}";
|
||||
"lng_bot_apps_which_link" = "Learn >";
|
||||
"lng_posts_title" = "Global Search";
|
||||
"lng_posts_start" = "Type a keyword to search all posts from public channels.";
|
||||
"lng_posts_subscribe" = "Subscribe to Premium";
|
||||
"lng_posts_need_subscribe" = "Global search is a Premium feature.";
|
||||
"lng_posts_search_button" = "Search {query}";
|
||||
"lng_posts_remaining#one" = "{count} free search remaining today";
|
||||
"lng_posts_remaining#other" = "{count} free searches remaining today";
|
||||
"lng_posts_subtitle_empty" = "Telegram News";
|
||||
"lng_posts_subtitle" = "Public posts";
|
||||
"lng_posts_limit_reached" = "Limit Reached";
|
||||
"lng_posts_limit_about#one" = "You can make up to {count} search query per day.";
|
||||
"lng_posts_limit_about#other" = "You can make up to {count} search queries per day.";
|
||||
"lng_posts_limit_search_paid" = "Search for {cost}";
|
||||
"lng_posts_limit_unlocks" = "free search unlocks in {duration}";
|
||||
"lng_posts_paid_spent#one" = "**{count} Star** spent on extra search.";
|
||||
"lng_posts_paid_spent#other" = "**{count} Stars** spent on extra search.";
|
||||
|
||||
"lng_popular_apps_info_title" = "Top Mini Apps";
|
||||
"lng_popular_apps_info_text" = "This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini apps in {bot} (as described {link}), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on the weekly average.";
|
||||
@@ -6756,6 +6964,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_frozen_text3" = "Appeal via {link} before {date}, or your account will be deleted.";
|
||||
"lng_frozen_appeal_button" = "Submit an Appeal";
|
||||
|
||||
"lng_age_verify_title" = "Age Verification";
|
||||
"lng_age_verify_mobile" = "Please open this media in the official Telegram app for Android or iOS to verify your age.";
|
||||
"lng_age_verify_here" = "This is a one-time process using your phone's camera. Your selfie will not be stored by Telegram.";
|
||||
"lng_age_verify_button" = "Verify My Age";
|
||||
"lng_age_verify_sorry_title" = "Age Check Failed";
|
||||
"lng_age_verify_sorry_text" = "Sorry, you can't view 18+ content.";
|
||||
|
||||
"lng_context_bank_card_copy" = "Copy Card Number";
|
||||
"lng_context_bank_card_copied" = "Card number copied to clipboard.";
|
||||
|
||||
@@ -6831,6 +7046,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"ayu_AyuPreferences" = "AyuGram Preferences";
|
||||
"ayu_DocsText" = "Documentation";
|
||||
"ayu_CategoryGeneral" = "General";
|
||||
"ayu_CategoryAppearance" = "Appearance";
|
||||
"ayu_CategoryChats" = "Chats";
|
||||
"ayu_CategoryOther" = "Other";
|
||||
"ayu_CategoryGhostMode" = "Ghost Mode";
|
||||
"ayu_CategorySpy" = "Spy";
|
||||
"ayu_CategoryFilters" = "Filters";
|
||||
@@ -6886,12 +7105,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_DisableAds" = "Disable Ads";
|
||||
"ayu_DisableStories" = "Disable Stories";
|
||||
"ayu_DisableCustomBackgrounds" = "Disable Custom Backgrounds";
|
||||
"ayu_SmoothScroll" = "Smooth Scroll";
|
||||
"ayu_DisableNotificationsDelay" = "Disable Notify Delay";
|
||||
"ayu_LocalPremium" = "Local Telegram Premium";
|
||||
"ayu_DisplayGhostStatus" = "Display Ghost Mode Status";
|
||||
"ayu_CopyUsernameAsLink" = "Copy Username as Link";
|
||||
"ayu_HideChannelReactions" = "Hide Reactions in Channels";
|
||||
"ayu_HideGroupReactions" = "Hide Reactions in Groups";
|
||||
"ayu_HideReactions" = "Hide Reactions";
|
||||
"ayu_HideReactionsInChannels" = "In Channels";
|
||||
"ayu_HideReactionsInGroups" = "In Groups";
|
||||
"ayu_QuickAdminShortcuts" = "Quick Admin Shortcuts";
|
||||
"ayu_TranslationProvider" = "Translation Provider";
|
||||
"ayu_LinksHeader" = "Links";
|
||||
"ayu_LinksChannel" = "Channel";
|
||||
"ayu_LinksChats" = "Chats";
|
||||
"ayu_LinksTranslate" = "Translate";
|
||||
"ayu_LinksDocumentation" = "Documentation";
|
||||
"ayu_CategoriesHeader" = "Categories";
|
||||
"ayu_SupportHeader" = "Support";
|
||||
"ayu_SupportDescription1" = "Support Development";
|
||||
"ayu_SupportDescription2" = "{item} and get an unique badge!";
|
||||
"ayu_SupportBoxHeader" = "Support Development";
|
||||
"ayu_SupportBoxInfo" = "By supporting the project, you not only contribute to its development but also get a unique badge.";
|
||||
"ayu_SupportBoxMakeDonationHeader" = "Make a Donation";
|
||||
"ayu_SupportBoxMakeDonationInfo" = "Transfer an amount of {amount1} ({amount2}) to any of the project's payment details. These can be found in the **Other** section of the app settings.";
|
||||
"ayu_SupportBoxSendProofHeader" = "Send Proof of Payment";
|
||||
"ayu_SupportBoxSendProofInfo" = "Send a photo of the payment confirmation to **{item}**. Make sure the photo clearly shows the amount, date, and time of the transfer.";
|
||||
"ayu_SupportBoxReceiveBadgeHeader" = "Receive Your Badge";
|
||||
"ayu_SupportBoxReceiveBadgeInfo" = "After payment verification, you will receive a unique badge that will be displayed on your profile and visible to other users.";
|
||||
"ayu_CrashReporting" = "Crash Reporting";
|
||||
"ayu_CrashReportingDescription" = "When this option is enabled, you'll be prompted to send a report after the app crashes. You can decide whether to send it or not.";
|
||||
"ayu_ResetSettings" = "Reset Settings";
|
||||
"ayu_ResetSettingsConfirmation" = "Are you sure you want to reset **all** AyuGram preferences to their defaults?";
|
||||
"ayu_DisableColorfulCover" = "Disable adaptive color";
|
||||
"ayu_EnableColorfulCover" = "Enable adaptive color";
|
||||
"ayu_CustomizationHeader" = "Customization";
|
||||
"ayu_DeletedMarkText" = "Deleted Mark";
|
||||
"ayu_DeletedMarkNothing" = "Nothing";
|
||||
@@ -6903,6 +7151,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_SemiTransparentDeletedMessages" = "Translucent Deleted Messages";
|
||||
"ayu_HideNotificationCounters" = "Hide Notification Counters";
|
||||
"ayu_HideNotificationBadge" = "Hide Notification Badge";
|
||||
"ayu_HideNotificationBadgeDescription" = "Hides the notification counter on the app icon in the taskbar and tray.";
|
||||
"ayu_HideAllChats" = "Hide \"All Chats\" Tab";
|
||||
"ayu_ChannelBottomButton" = "Channel Bottom Button";
|
||||
"ayu_ChannelBottomButtonHide" = "Hide";
|
||||
@@ -6911,8 +7160,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_SettingsShowID" = "Show Peer ID";
|
||||
"ayu_SettingsShowID_Hide" = "Hide";
|
||||
"ayu_SettingsShowMessageShot" = "Show Message Shot";
|
||||
"ayu_SettingsShowMessageShotDescription" = "Allows taking a screenshot of the selected messages.";
|
||||
"ayu_SettingsRecentStickersCount" = "Recent Stickers Count";
|
||||
"ayu_SettingsCustomizationHint" = "You must restart the application after making changes in the \"Customization\" section.";
|
||||
"ayu_MaterialSwitches" = "MD3 Switch Style";
|
||||
"ayu_RemoveMessageTail" = "Remove Message Tail";
|
||||
"ayu_HideShareButton" = "Hide Side \"Share\" Button";
|
||||
"ayu_ChatFoldersHeader" = "Chat Folders";
|
||||
"ayu_SettingsContextMenuTitle" = "Choose when to show the item";
|
||||
"ayu_SettingsContextMenuDescription" = "Extended menu items will be displayed if you hold CTRL or SHIFT while right-clicking on the message.";
|
||||
"ayu_SettingsContextMenuItemHidden" = "Hidden";
|
||||
@@ -6931,6 +7185,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_DrawerElementsHeader" = "Drawer Elements";
|
||||
"ayu_TrayElementsHeader" = "Tray Elements";
|
||||
"ayu_SettingsWideMultiplier" = "Wide Messages Multiplier";
|
||||
"ayu_SettingsWideMultiplierDescription" = "You can change message width for better display on wide monitors.";
|
||||
"ayu_SettingsSpoofWebviewAsAndroid" = "Spoof Client as Android";
|
||||
"ayu_SettingsBiggerWindow" = "Bigger Window";
|
||||
"ayu_SettingsIncreaseWebviewHeight" = "Increase Content Height";
|
||||
@@ -6949,9 +7204,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_ImportDataSuccess" = "Database imported successfully.";
|
||||
"ayu_ImportDataFailure" = "Failed to import database.";
|
||||
"ayu_RegexFilters" = "Message Filters";
|
||||
"ayu_RegexFiltersAmount#one" = "1 filter";
|
||||
"ayu_RegexFiltersAmount#one" = "{count} filter";
|
||||
"ayu_RegexFiltersAmount#other" = "{count} filters";
|
||||
"ayu_RegexFiltersExcludedAmount#one" = "1 excluded";
|
||||
"ayu_RegexFiltersExcludedAmount#one" = "{count} excluded";
|
||||
"ayu_RegexFiltersExcludedAmount#other" = "{count} excluded";
|
||||
"ayu_RegexFiltersHeader" = "Filters";
|
||||
"ayu_RegexFiltersShared" = "Shared Filters";
|
||||
@@ -6991,22 +7246,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_FiltersToastFailNoChanges" = "Filters are the same as current.";
|
||||
"ayu_FiltersToastSuccess" = "Filters successfully imported.";
|
||||
"ayu_FiltersSheetTitle" = "Apply filter changes?";
|
||||
"ayu_FiltersSheetNewFilters#one" = "**1** new filter";
|
||||
"ayu_FiltersSheetNewFilters#one" = "**{count}** new filter";
|
||||
"ayu_FiltersSheetNewFilters#other" = "**{count}** new filters";
|
||||
"ayu_FiltersSheetRemovedFilters#one" = "**1** removed filter";
|
||||
"ayu_FiltersSheetRemovedFilters#one" = "**{count}** removed filter";
|
||||
"ayu_FiltersSheetRemovedFilters#other" = "**{count}** removed filters";
|
||||
"ayu_FiltersSheetUpdatedFilters#one" = "**1** updated filter";
|
||||
"ayu_FiltersSheetUpdatedFilters#one" = "**{count}** updated filter";
|
||||
"ayu_FiltersSheetUpdatedFilters#other" = "**{count}** updated filters";
|
||||
"ayu_FiltersSheetNewExclusions#one" = "**1** new exclusion";
|
||||
"ayu_FiltersSheetNewExclusions#one" = "**{count}** new exclusion";
|
||||
"ayu_FiltersSheetNewExclusions#other" = "**{count}** new exclusions";
|
||||
"ayu_FiltersSheetRemovedExclusions#one" = "**1** removed exclusion";
|
||||
"ayu_FiltersSheetRemovedExclusions#one" = "**{count}** removed exclusion";
|
||||
"ayu_FiltersSheetRemovedExclusions#other" = "**{count}** removed exclusions";
|
||||
"ayu_FiltersSheetDialogsToResolve#one" = "**1** dialog to resolve";
|
||||
"ayu_FiltersSheetDialogsToResolve#one" = "**{count}** dialog to resolve";
|
||||
"ayu_FiltersSheetDialogsToResolve#other" = "**{count}** dialogs to resolve";
|
||||
"ayu_FiltersClearPopupTitle" = "Clear filters";
|
||||
"ayu_FiltersClearPopupText" = "Are you sure you want to clear all filters?";
|
||||
"ayu_FiltersClearPopupActionText" = "Clear";
|
||||
"ayu_FiltersClearPopupAltActionText" = "Clear unknown";
|
||||
"ayu_AppIconHeader" = "App Icon";
|
||||
"ayu_IconDefault" = "Default";
|
||||
"ayu_IconAlternative" = "AyuGram Alt";
|
||||
"ayu_IconDiscord" = "Discord";
|
||||
@@ -7147,8 +7403,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_SessionTerminated" = "Session **{item}** was terminated. You may still browse cached messages.";
|
||||
"ayu_LocalPremiumNotice" = "You're using **local** Telegram Premium.\nIt **won't** give you any benefits.\n**Enjoy the star near your nickname!**";
|
||||
"ayu_DeveloperPopup" = "**{item}** is a member of the **exteraGram** development team.";
|
||||
"ayu_SupporterPopup" = "**{item}** supported the development of **exteraGram** or **AyuGram** and received an exclusive badge.";
|
||||
"ayu_SupporterPopup" = "**{item}** supported the development of **exteraGram** or **AyuGram** and received an unique badge.";
|
||||
"ayu_OfficialResourcePopup" = "**{item}** is the official resource of **exteraGram** or **AyuGram**.";
|
||||
"ayu_SettingsWatermark" = "AyuGram is developed and maintained by Radolyn Labs.";
|
||||
"ayu_SettingsDescription" = "Telegram Desktop fork focused on customization and ToS-breaking features.";
|
||||
"ayu_ConfirmationSticker" = "Do you want to send this sticker?";
|
||||
"ayu_ConfirmationGIF" = "Do you want to send this GIF?";
|
||||
"ayu_ConfirmationVoice" = "Do you want to send this voice message?";
|
||||
|
||||
@@ -51,8 +51,8 @@ var LocationPicker = {
|
||||
},
|
||||
init: function (params) {
|
||||
mapboxgl.accessToken = params.token;
|
||||
if (params.protocol) {
|
||||
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
|
||||
if (location.hostname != 'desktop-app-resource') {
|
||||
mapboxgl.config.API_URL = location.protocol + '//' + location.host + '/api.mapbox.com';
|
||||
}
|
||||
|
||||
var options = { container: 'map', config: {
|
||||
|
||||
@@ -52,5 +52,13 @@
|
||||
<file alias="art/ayu/yaplus/app_preview.png">../../art/ayu/yaplus/app_preview.png</file>
|
||||
<file alias="art/ayu/yaplus/app_macos.png">../../art/ayu/yaplus/app_macos.png</file>
|
||||
<file alias="art/ayu/yaplus/app_icon.ico">../../art/ayu/yaplus/app_icon.ico</file>
|
||||
<file alias="icons/ayu/nocover.svg">../../icons/ayu/nocover.svg</file>
|
||||
<file alias="icons/ayu/donates/boosty.svg">../../icons/ayu/donates/boosty.svg</file>
|
||||
<file alias="icons/ayu/donates/ton.svg">../../icons/ayu/donates/ton.svg</file>
|
||||
<file alias="icons/ayu/donates/bitcoin.svg">../../icons/ayu/donates/bitcoin.svg</file>
|
||||
<file alias="icons/ayu/donates/ethereum.svg">../../icons/ayu/donates/ethereum.svg</file>
|
||||
<file alias="icons/ayu/donates/solana.svg">../../icons/ayu/donates/solana.svg</file>
|
||||
<file alias="icons/ayu/donates/tron.svg">../../icons/ayu/donates/tron.svg</file>
|
||||
<file alias="icons/ayu/donates/support_logo.svg">../../icons/ayu/donates/support_logo.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<file alias="topics_list.tgs">../../animations/edit_peers/topics_list.tgs</file>
|
||||
<file alias="direct_messages.tgs">../../animations/edit_peers/direct_messages.tgs</file>
|
||||
<file alias="no_chats.tgs">../../animations/no_chats.tgs</file>
|
||||
<file alias="transcribe_loading.tgs">../../animations/transcribe_loading.tgs</file>
|
||||
|
||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
<file alias="group_call_connect.mp3">../../sounds/group_call_connect.mp3</file>
|
||||
<file alias="group_call_end.mp3">../../sounds/group_call_end.mp3</file>
|
||||
<file alias="group_call_allowed.mp3">../../sounds/group_call_allowed.mp3</file>
|
||||
<file alias="group_call_recording_start.mp3">../../sounds/group_call_recording_start.mp3</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="5.16.4.0" />
|
||||
Version="6.1.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,16,4,0
|
||||
PRODUCTVERSION 5,16,4,0
|
||||
FILEVERSION 6,1,3,0
|
||||
PRODUCTVERSION 6,1,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop"
|
||||
VALUE "FileVersion", "5.16.4.0"
|
||||
VALUE "FileVersion", "6.1.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.16.4.0"
|
||||
VALUE "ProductVersion", "6.1.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,16,4,0
|
||||
PRODUCTVERSION 5,16,4,0
|
||||
FILEVERSION 6,1,3,0
|
||||
PRODUCTVERSION 6,1,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||
VALUE "FileVersion", "5.16.4.0"
|
||||
VALUE "FileVersion", "6.1.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.16.4.0"
|
||||
VALUE "ProductVersion", "6.1.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/application.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@@ -83,7 +86,19 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
} // namespace
|
||||
|
||||
Authorizations::Authorizations(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance()) {
|
||||
: _api(&api->instance())
|
||||
, _autoconfirmPeriod([=] {
|
||||
constexpr auto kFallbackCount = 604800;
|
||||
return api->session().appConfig().get<int>(
|
||||
u"authorization_autoconfirm_period"_q,
|
||||
kFallbackCount);
|
||||
})
|
||||
, _saveUnreviewed([=] {
|
||||
api->session().settings().setUnreviewed(_unreviewed);
|
||||
api->session().saveSettingsDelayed();
|
||||
}) {
|
||||
_unreviewed = api->session().settings().unreviewed();
|
||||
removeExpiredUnreviewed();
|
||||
Core::App().settings().deviceModelChanges(
|
||||
) | rpl::start_with_next([=](const QString &model) {
|
||||
auto changed = false;
|
||||
@@ -119,6 +134,7 @@ void Authorizations::reload() {
|
||||
) | ranges::views::transform([](const MTPAuthorization &auth) {
|
||||
return ParseEntry(auth.data());
|
||||
}) | ranges::to<List>;
|
||||
removeExpiredUnreviewed();
|
||||
refreshCallsDisabledHereFromCloud();
|
||||
_listChanges.fire({});
|
||||
}).fail([=] {
|
||||
@@ -217,11 +233,7 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) {
|
||||
MTP_bool(disabled)
|
||||
)).done([=] {
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
LOG(("API Error: toggle calls %1. Hash: %2. %3.")
|
||||
.arg(disabled ? u"disabled"_q : u"enabled"_q)
|
||||
.arg(hash)
|
||||
.arg(error.type()));
|
||||
}).fail([=] {
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).send();
|
||||
_toggleCallsDisabledRequests.emplace(hash, id);
|
||||
@@ -265,4 +277,129 @@ crl::time Authorizations::lastReceivedTime() {
|
||||
return _lastReceived;
|
||||
}
|
||||
|
||||
const std::vector<Data::UnreviewedAuth> &Authorizations::unreviewed() {
|
||||
removeExpiredUnreviewed();
|
||||
return _unreviewed;
|
||||
}
|
||||
|
||||
void Authorizations::removeExpiredUnreviewed() {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto period = _autoconfirmPeriod();
|
||||
|
||||
const auto oldSize = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[=](const auto &auth) {
|
||||
return (now - auth.date) >= period;
|
||||
}),
|
||||
_unreviewed.end());
|
||||
|
||||
if (_unreviewed.size() != oldSize) {
|
||||
_saveUnreviewed();
|
||||
}
|
||||
}
|
||||
|
||||
void Authorizations::review(const std::vector<uint64> &hashes, bool confirm) {
|
||||
for (const auto hash : hashes) {
|
||||
if (const auto sent = _reviewRequests.take(hash)) {
|
||||
_api.request(*sent).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
const auto checkComplete = [=] {
|
||||
if (_reviewRequests.empty()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto hash : hashes) {
|
||||
const auto removeFromUnreviewed = [=] {
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
_reviewRequests.remove(hash);
|
||||
checkComplete();
|
||||
};
|
||||
|
||||
if (confirm) {
|
||||
using Flag = MTPaccount_ChangeAuthorizationSettings::Flag;
|
||||
const auto id = _api.request(MTPaccount_ChangeAuthorizationSettings(
|
||||
MTP_flags(Flag::f_confirmed),
|
||||
MTP_long(hash),
|
||||
MTPBool(), // encrypted_requests_disabled
|
||||
MTPBool() // call_requests_disabled
|
||||
)).done([=] {
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
} else {
|
||||
const auto id = _api.request(MTPaccount_ResetAuthorization(
|
||||
MTP_long(hash)
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
_list.erase(
|
||||
ranges::remove(_list, hash, &Entry::hash),
|
||||
end(_list));
|
||||
_listChanges.fire({});
|
||||
}
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> Authorizations::unreviewedChanges() const {
|
||||
return _unreviewedChanges.events();
|
||||
}
|
||||
|
||||
void Authorizations::apply(const MTPUpdate &update) {
|
||||
removeExpiredUnreviewed();
|
||||
update.match([&](const MTPDupdateNewAuthorization &data) {
|
||||
auto unreviewed = Data::UnreviewedAuth{
|
||||
.hash = data.vhash().v,
|
||||
.unconfirmed = data.is_unconfirmed(),
|
||||
.date = data.vdate().value_or_empty(),
|
||||
.device = qs(data.vdevice().value_or_empty()),
|
||||
.location = qs(data.vlocation().value_or_empty())
|
||||
};
|
||||
if (!unreviewed.unconfirmed) {
|
||||
const auto hash = unreviewed.hash;
|
||||
const auto was = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(
|
||||
_unreviewed.begin(),
|
||||
_unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
if (was != _unreviewed.size()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &auth : _unreviewed) {
|
||||
if (auth.hash == unreviewed.hash) {
|
||||
auth = std::move(unreviewed);
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_unreviewed.push_back(std::move(unreviewed));
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}, [](auto&&) {
|
||||
Unexpected("Update in Authorizations::apply.");
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_authorization.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
@@ -35,6 +36,8 @@ public:
|
||||
Fn<void(const MTP::Error &error)> &&fail,
|
||||
std::optional<uint64> hash = std::nullopt);
|
||||
|
||||
void apply(const MTPUpdate &update);
|
||||
|
||||
[[nodiscard]] crl::time lastReceivedTime();
|
||||
|
||||
[[nodiscard]] List list() const;
|
||||
@@ -42,6 +45,11 @@ public:
|
||||
[[nodiscard]] int total() const;
|
||||
[[nodiscard]] rpl::producer<int> totalValue() const;
|
||||
|
||||
[[nodiscard]] const std::vector<Data::UnreviewedAuth> &unreviewed();
|
||||
[[nodiscard]] rpl::producer<> unreviewedChanges() const;
|
||||
|
||||
void review(const std::vector<uint64> &hashes, bool confirm);
|
||||
|
||||
void updateTTL(int days);
|
||||
[[nodiscard]] rpl::producer<int> ttlDays() const;
|
||||
|
||||
@@ -57,6 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
void refreshCallsDisabledHereFromCloud();
|
||||
void removeExpiredUnreviewed();
|
||||
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -64,10 +73,16 @@ private:
|
||||
List _list;
|
||||
rpl::event_stream<> _listChanges;
|
||||
|
||||
Fn<int()> _autoconfirmPeriod;
|
||||
std::vector<Data::UnreviewedAuth> _unreviewed;
|
||||
rpl::event_stream<> _unreviewedChanges;
|
||||
Fn<void()> _saveUnreviewed;
|
||||
|
||||
mtpRequestId _ttlRequestId = 0;
|
||||
rpl::variable<int> _ttlDays = 0;
|
||||
|
||||
base::flat_map<uint64, mtpRequestId> _toggleCallsDisabledRequests;
|
||||
base::flat_map<uint64, mtpRequestId> _reviewRequests;
|
||||
rpl::variable<bool> _callsDisabledHere;
|
||||
|
||||
crl::time _lastReceived = 0;
|
||||
|
||||
@@ -245,7 +245,7 @@ void SendBotCallbackDataWithPassword(
|
||||
fields.customSubmitButton = tr::lng_passcode_submit();
|
||||
fields.customCheckCallback = [=](
|
||||
const Core::CloudPasswordResult &result,
|
||||
QPointer<PasscodeBox> box) {
|
||||
base::weak_qptr<PasscodeBox> box) {
|
||||
if (const auto button = getButton()) {
|
||||
if (button->requestId) {
|
||||
return;
|
||||
|
||||
@@ -57,7 +57,7 @@ void RemoveChatFilter(
|
||||
RemoveComplexChatFilter::RemoveComplexChatFilter() = default;
|
||||
|
||||
void RemoveComplexChatFilter::request(
|
||||
QPointer<Ui::RpWidget> widget,
|
||||
base::weak_qptr<Ui::RpWidget> widget,
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
FilterId id) {
|
||||
const auto session = &weak->session();
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
RemoveComplexChatFilter();
|
||||
|
||||
void request(
|
||||
QPointer<Ui::RpWidget> widget,
|
||||
base::weak_qptr<Ui::RpWidget> widget,
|
||||
base::weak_ptr<Window::SessionController> weak,
|
||||
FilterId id);
|
||||
|
||||
|
||||
@@ -142,13 +142,12 @@ void ConfirmSubscriptionBox(
|
||||
const auto content = box->verticalLayout();
|
||||
|
||||
Ui::AddSkip(content, st::confirmInvitePhotoTop);
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::RpWidget>(content)));
|
||||
const auto userpic = userpicWrap->entity();
|
||||
const auto userpic = content->add(
|
||||
object_ptr<Ui::RpWidget>(content),
|
||||
style::al_top);
|
||||
const auto photoSize = st::confirmInvitePhotoSize;
|
||||
userpic->resize(Size(photoSize));
|
||||
userpic->setNaturalWidth(photoSize);
|
||||
const auto creditsIconSize = photoSize / 3;
|
||||
const auto creditsIconCallback =
|
||||
Ui::PaintOutlinedColoredCreditsIconCallback(
|
||||
@@ -188,8 +187,8 @@ void ConfirmSubscriptionBox(
|
||||
}
|
||||
auto p = QPainter(userpic);
|
||||
p.drawImage(0, 0, state->frame);
|
||||
}, userpicWrap->lifetime());
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}, userpic->lifetime());
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (photo) {
|
||||
state->photoMedia = photo->createMediaView();
|
||||
state->photoMedia->wanted(Data::PhotoSize::Small, Data::FileOrigin());
|
||||
@@ -197,7 +196,7 @@ void ConfirmSubscriptionBox(
|
||||
session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
userpic->update();
|
||||
}, userpicWrap->entity()->lifetime());
|
||||
}, userpic->lifetime());
|
||||
}
|
||||
} else {
|
||||
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
@@ -215,43 +214,40 @@ void ConfirmSubscriptionBox(
|
||||
2.);
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle)));
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle),
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_about(
|
||||
lt_channel,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
lt_price,
|
||||
tr::lng_credits_summary_options_credits(
|
||||
lt_count,
|
||||
rpl::single(amount) | tr::to_count(),
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout)));
|
||||
tr::lng_channel_invite_subscription_about(
|
||||
lt_channel,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
lt_price,
|
||||
tr::lng_credits_summary_options_credits(
|
||||
lt_count,
|
||||
rpl::single(amount) | tr::to_count(),
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_terms(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_paid_react_agree_link(),
|
||||
tr::lng_group_invite_subscription_about_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms)));
|
||||
tr::lng_channel_invite_subscription_terms(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_paid_react_agree_link(),
|
||||
tr::lng_group_invite_subscription_about_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms),
|
||||
style::al_top);
|
||||
|
||||
{
|
||||
const auto balance = Settings::AddBalanceWidget(
|
||||
@@ -272,7 +268,7 @@ void ConfirmSubscriptionBox(
|
||||
}, balance->lifetime());
|
||||
}
|
||||
|
||||
const auto sendCredits = [=, weak = Ui::MakeWeak(box)] {
|
||||
const auto sendCredits = [=, weak = base::make_weak(box)] {
|
||||
const auto show = box->uiShow();
|
||||
const auto buttonWidth = state->saveButton
|
||||
? state->saveButton->width()
|
||||
@@ -280,7 +276,7 @@ void ConfirmSubscriptionBox(
|
||||
const auto finish = [=] {
|
||||
state->api = std::nullopt;
|
||||
state->loading.force_assign(false);
|
||||
if (const auto strong = weak.data()) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
@@ -294,7 +290,7 @@ void ConfirmSubscriptionBox(
|
||||
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||
});
|
||||
const auto refill = session->data().activeCreditsSubsRebuilder();
|
||||
const auto strong = weak.data();
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class ChannelData;
|
||||
|
||||
namespace Info::Profile {
|
||||
class Badge;
|
||||
enum class BadgeType : uchar;
|
||||
enum class BadgeType : ushort;
|
||||
} // namespace Info::Profile
|
||||
|
||||
namespace Main {
|
||||
|
||||
@@ -87,7 +87,7 @@ void ConfirmPhone::resolve(
|
||||
sentCodeLength,
|
||||
fragmentUrl,
|
||||
timeout);
|
||||
const auto boxWeak = Ui::MakeWeak(box.data());
|
||||
const auto boxWeak = base::make_weak(box.data());
|
||||
using LoginCode = rpl::event_stream<QString>;
|
||||
const auto codeHandles = box->lifetime().make_state<LoginCode>();
|
||||
controller->session().account().setHandleLoginCode([=](
|
||||
|
||||
@@ -153,6 +153,7 @@ Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
|
||||
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
|
||||
.converted = stargift && incoming,
|
||||
.stargift = stargift.has_value(),
|
||||
.postsSearch = tl.data().is_posts_search(),
|
||||
.giftUpgraded = tl.data().is_stargift_upgrade(),
|
||||
.giftResale = tl.data().is_stargift_resale(),
|
||||
.reaction = tl.data().is_reaction(),
|
||||
|
||||
@@ -82,7 +82,7 @@ void HandleWithdrawalButton(
|
||||
fields.customSubmitButton = tr::lng_passcode_submit();
|
||||
fields.customCheckCallback = crl::guard(button, [=](
|
||||
const Core::CloudPasswordResult &result,
|
||||
QPointer<PasscodeBox> box) {
|
||||
base::weak_qptr<PasscodeBox> box) {
|
||||
const auto done = [=](const QString &result) {
|
||||
if (!result.isEmpty()) {
|
||||
UrlClickHandler::Open(result);
|
||||
|
||||
@@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "dialogs/ui/chat_search_in.h" // IsHashOrCashtagSearchQuery
|
||||
#include "main/main_session.h"
|
||||
|
||||
// AyuGram includes
|
||||
#include "ayu/ayu_settings.h"
|
||||
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
@@ -93,6 +97,11 @@ void PeerSearch::requestSponsored() {
|
||||
).done([=](
|
||||
const MTPcontacts_SponsoredPeers &result,
|
||||
mtpRequestId requestId) {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
if (settings.disableAds) {
|
||||
finishSponsored(requestId, PeerSearchResult{});
|
||||
return;
|
||||
}
|
||||
result.match([&](const MTPDcontacts_sponsoredPeersEmpty &) {
|
||||
finishSponsored(requestId, PeerSearchResult{});
|
||||
}, [&](const MTPDcontacts_sponsoredPeers &data) {
|
||||
|
||||
@@ -45,19 +45,45 @@ namespace {
|
||||
auto options = PremiumSubscriptionOptionsFromTL(tlOptions);
|
||||
for (auto i = 0; i < options.size(); i++) {
|
||||
const auto &tlOption = tlOptions[i].data();
|
||||
const auto currency = qs(tlOption.vcurrency());
|
||||
const auto perUserText = Ui::FillAmountAndCurrency(
|
||||
tlOption.vamount().v / float64(tlOption.vusers().v),
|
||||
qs(tlOption.vcurrency()),
|
||||
currency,
|
||||
false);
|
||||
options[i].costPerMonth = perUserText
|
||||
+ ' '
|
||||
+ QChar(0x00D7)
|
||||
+ ' '
|
||||
+ QString::number(tlOption.vusers().v);
|
||||
options[i].currency = currency;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
[[nodiscard]] int FindStarsForResale(const MTPVector<MTPStarsAmount> *list) {
|
||||
if (!list) {
|
||||
return 0;
|
||||
}
|
||||
for (const auto &amount : list->v) {
|
||||
if (amount.type() == mtpc_starsAmount) {
|
||||
return int(amount.c_starsAmount().vamount().v);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] int64 FindTonForResale(const MTPVector<MTPStarsAmount> *list) {
|
||||
if (!list) {
|
||||
return 0;
|
||||
}
|
||||
for (const auto &amount : list->v) {
|
||||
if (amount.type() == mtpc_starsTonAmount) {
|
||||
return int64(amount.c_starsTonAmount().vamount().v);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Premium::Premium(not_null<ApiWrap*> api)
|
||||
@@ -589,24 +615,32 @@ std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
|
||||
const auto it = _subscriptionOptions.find(amount);
|
||||
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::optionsForGiveaway(
|
||||
int usersCount) {
|
||||
const auto skipForStars = [&](Data::PremiumSubscriptionOptions options) {
|
||||
const auto proj = &Data::PremiumSubscriptionOption::currency;
|
||||
options.erase(
|
||||
ranges::remove(options, Ui::kCreditsCurrency, proj),
|
||||
end(options));
|
||||
return options;
|
||||
};
|
||||
const auto it = _subscriptionOptions.find(usersCount);
|
||||
if (it != end(_subscriptionOptions)) {
|
||||
return it->second;
|
||||
return skipForStars(it->second);
|
||||
} else {
|
||||
auto tlOptions = QVector<MTPPremiumGiftCodeOption>();
|
||||
for (auto i = 0; i < _optionsForOnePerson.months.size(); i++) {
|
||||
tlOptions.push_back(MTP_premiumGiftCodeOption(
|
||||
MTP_flags(MTPDpremiumGiftCodeOption::Flags(0)),
|
||||
MTP_int(amount),
|
||||
MTP_int(usersCount),
|
||||
MTP_int(_optionsForOnePerson.months[i]),
|
||||
MTPstring(),
|
||||
MTPint(),
|
||||
MTP_string(_optionsForOnePerson.currencies[i]),
|
||||
MTP_long(_optionsForOnePerson.totalCosts[i] * amount)));
|
||||
MTP_long(_optionsForOnePerson.totalCosts[i] * usersCount)));
|
||||
}
|
||||
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
|
||||
return _subscriptionOptions[amount];
|
||||
_subscriptionOptions[usersCount] = GiftCodesFromTL(tlOptions);
|
||||
return skipForStars(_subscriptionOptions[usersCount]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,8 +859,12 @@ std::optional<Data::StarGift> FromTL(
|
||||
.resellCount = int(data.vavailability_resale().value_or_empty()),
|
||||
.limitedLeft = remaining.value_or_empty(),
|
||||
.limitedCount = total.value_or_empty(),
|
||||
.perUserTotal = data.vper_user_total().value_or_empty(),
|
||||
.perUserRemains = data.vper_user_remains().value_or_empty(),
|
||||
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
|
||||
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
|
||||
.lockedUntilDate = data.vlocked_until_date().value_or_empty(),
|
||||
.requirePremium = data.is_require_premium(),
|
||||
.upgradable = data.vupgrade_stars().has_value(),
|
||||
.birthday = data.is_birthday(),
|
||||
.soldOut = data.is_sold_out(),
|
||||
@@ -853,13 +891,20 @@ std::optional<Data::StarGift> FromTL(
|
||||
const auto releasedById = data.vreleased_by()
|
||||
? peerFromMTP(*data.vreleased_by())
|
||||
: PeerId();
|
||||
const auto themeUserId = data.vtheme_peer()
|
||||
? peerFromMTP(*data.vtheme_peer())
|
||||
: PeerId();
|
||||
const auto releasedBy = releasedById
|
||||
? session->data().peer(releasedById).get()
|
||||
: nullptr;
|
||||
const auto themeUser = themeUserId
|
||||
? session->data().peer(themeUserId).get()
|
||||
: nullptr;
|
||||
auto result = Data::StarGift{
|
||||
.id = uint64(data.vid().v),
|
||||
.id = data.vid().v,
|
||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||
.id = data.vid().v,
|
||||
.initialGiftId = data.vgift_id().v,
|
||||
.slug = qs(data.vslug()),
|
||||
.title = qs(data.vtitle()),
|
||||
.ownerAddress = qs(data.vowner_address().value_or_empty()),
|
||||
@@ -868,15 +913,30 @@ std::optional<Data::StarGift> FromTL(
|
||||
? peerFromMTP(*data.vowner_id())
|
||||
: PeerId()),
|
||||
.releasedBy = releasedBy,
|
||||
.themeUser = themeUser,
|
||||
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
|
||||
.starsForResale = FindStarsForResale(data.vresell_amount()),
|
||||
.number = data.vnum().v,
|
||||
.starsForResale = int(data.vresell_stars().value_or_empty()),
|
||||
.onlyAcceptTon = data.is_resale_ton_only(),
|
||||
.canBeTheme = data.is_theme_available(),
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
.value = (data.vvalue_amount()
|
||||
? std::make_shared<Data::UniqueGiftValue>(
|
||||
Data::UniqueGiftValue{
|
||||
.currency = qs(
|
||||
data.vvalue_currency().value_or_empty()),
|
||||
.valuePrice = int64(
|
||||
data.vvalue_amount().value_or_empty()),
|
||||
})
|
||||
: nullptr),
|
||||
}),
|
||||
.document = model->document,
|
||||
.releasedBy = releasedBy,
|
||||
.limitedLeft = (total - data.vavailability_issued().v),
|
||||
.limitedCount = total,
|
||||
.resellTonOnly = data.is_resale_ton_only(),
|
||||
.requirePremium = data.is_require_premium(),
|
||||
};
|
||||
const auto unique = result.unique.get();
|
||||
for (const auto &attribute : data.vattributes().v) {
|
||||
@@ -913,21 +973,26 @@ std::optional<Data::SavedStarGift> FromTL(
|
||||
.manageId = (to->isUser()
|
||||
? Id::User(data.vmsg_id().value_or_empty())
|
||||
: Id::Chat(to, data.vsaved_id().value_or_empty())),
|
||||
.collectionIds = (data.vcollection_id()
|
||||
? (data.vcollection_id()->v
|
||||
| ranges::views::transform(&MTPint::v)
|
||||
| ranges::to_vector)
|
||||
: std::vector<int>()),
|
||||
.message = (data.vmessage()
|
||||
? TextWithEntities{
|
||||
.text = qs(data.vmessage()->data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
session,
|
||||
data.vmessage()->data().ventities().v),
|
||||
}
|
||||
? Api::ParseTextWithEntities(
|
||||
session,
|
||||
*data.vmessage())
|
||||
: TextWithEntities()),
|
||||
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
||||
.starsUpgradedBySender = int64(
|
||||
data.vupgrade_stars().value_or_empty()),
|
||||
.giftPrepayUpgradeHash = qs(
|
||||
data.vprepaid_upgrade_hash().value_or_empty()),
|
||||
.fromId = (data.vfrom_id()
|
||||
? peerFromMTP(*data.vfrom_id())
|
||||
: PeerId()),
|
||||
.date = data.vdate().v,
|
||||
.upgradeSeparate = data.is_upgrade_separate(),
|
||||
.upgradable = data.is_can_upgrade(),
|
||||
.anonymous = data.is_name_hidden(),
|
||||
.pinned = data.is_pinned_to_top() && hasUnique,
|
||||
|
||||
@@ -180,7 +180,8 @@ public:
|
||||
|
||||
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
|
||||
[[nodiscard]] std::vector<GiftOptionData> optionsForPeer() const;
|
||||
[[nodiscard]] Data::PremiumSubscriptionOptions options(int amount);
|
||||
[[nodiscard]] Data::PremiumSubscriptionOptions optionsForGiveaway(
|
||||
int usersCount);
|
||||
[[nodiscard]] const std::vector<int> &availablePresets() const;
|
||||
[[nodiscard]] int monthsFromPreset(int monthsIndex);
|
||||
[[nodiscard]] Payments::InvoicePremiumGiftCode invoice(
|
||||
|
||||
@@ -25,7 +25,7 @@ SensitiveContent::SensitiveContent(not_null<ApiWrap*> api)
|
||||
}
|
||||
|
||||
void SensitiveContent::preload() {
|
||||
if (!_loaded) {
|
||||
if (!_loaded && !_loadRequestId) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,6 @@ void SensitiveContent::reload(bool force) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
_loaded = true;
|
||||
_loadRequestId = _api.request(MTPaccount_GetContentSettings(
|
||||
)).done([=](const MTPaccount_ContentSettings &result) {
|
||||
_loadRequestId = 0;
|
||||
@@ -50,6 +49,10 @@ void SensitiveContent::reload(bool force) {
|
||||
_enabled = enabled;
|
||||
_canChange = canChange;
|
||||
}
|
||||
if (!_loaded) {
|
||||
_loaded = true;
|
||||
_loadedChanged.fire({});
|
||||
}
|
||||
if (base::take(_appConfigReloadForce) || changed) {
|
||||
_appConfigReloadTimer.callOnce(kRefreshAppConfigTimeout);
|
||||
}
|
||||
@@ -61,6 +64,19 @@ void SensitiveContent::reload(bool force) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
bool SensitiveContent::loaded() const {
|
||||
return _loaded;
|
||||
}
|
||||
|
||||
rpl::producer<bool> SensitiveContent::loadedValue() const {
|
||||
if (_loaded) {
|
||||
return rpl::single(true);
|
||||
}
|
||||
return rpl::single(false) | rpl::then(
|
||||
_loadedChanged.events() | rpl::map_to(true)
|
||||
);
|
||||
}
|
||||
|
||||
bool SensitiveContent::enabledCurrent() const {
|
||||
return _enabled.current();
|
||||
}
|
||||
@@ -69,6 +85,10 @@ rpl::producer<bool> SensitiveContent::enabled() const {
|
||||
return _enabled.value();
|
||||
}
|
||||
|
||||
bool SensitiveContent::canChangeCurrent() const {
|
||||
return _canChange.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> SensitiveContent::canChange() const {
|
||||
return _canChange.value();
|
||||
}
|
||||
|
||||
@@ -26,12 +26,16 @@ public:
|
||||
void reload(bool force = false);
|
||||
void update(bool enabled);
|
||||
|
||||
[[nodiscard]] bool loaded() const;
|
||||
[[nodiscard]] rpl::producer<bool> loadedValue() const;
|
||||
[[nodiscard]] bool enabledCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> enabled() const;
|
||||
[[nodiscard]] bool canChangeCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> canChange() const;
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
rpl::event_stream<> _loadedChanged;
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _loadRequestId = 0;
|
||||
mtpRequestId _saveRequestId = 0;
|
||||
|
||||
@@ -223,7 +223,7 @@ void ConfirmApproval(
|
||||
.confirmText = tr::lng_suggest_accept_send(),
|
||||
.title = tr::lng_suggest_accept_title(),
|
||||
});
|
||||
*callback = [=, weak = Ui::MakeWeak(box)] {
|
||||
*callback = [=, weak = base::make_weak(box)] {
|
||||
if (const auto onstack = accepted) {
|
||||
onstack();
|
||||
}
|
||||
@@ -232,7 +232,7 @@ void ConfirmApproval(
|
||||
return;
|
||||
}
|
||||
SendApproval(show, item, scheduleDate);
|
||||
if (const auto strong = weak.data()) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
@@ -283,9 +283,9 @@ void RequestApprovalDate(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto id = item->fullId();
|
||||
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto weak = std::make_shared<base::weak_qptr<Ui::BoxContent>>();
|
||||
const auto close = [=] {
|
||||
if (const auto strong = weak->data()) {
|
||||
if (const auto strong = weak->get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
@@ -339,13 +339,13 @@ void RequestDeclineComment(
|
||||
box->setFocusCallback([=] {
|
||||
reason->setFocusFast();
|
||||
});
|
||||
*callback = [=, weak = Ui::MakeWeak(box)] {
|
||||
*callback = [=, weak = base::make_weak(box)] {
|
||||
const auto item = show->session().data().message(id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
SendDecline(show, item, reason->getLastText().trimmed());
|
||||
if (const auto strong = weak.data()) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
@@ -422,14 +422,14 @@ void SuggestApprovalDate(
|
||||
}
|
||||
const auto id = item->fullId();
|
||||
const auto state = std::make_shared<SendSuggestState>();
|
||||
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto weak = std::make_shared<base::weak_qptr<Ui::BoxContent>>();
|
||||
const auto done = [=](TimeId result) {
|
||||
const auto item = show->session().data().message(id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto close = [=] {
|
||||
if (const auto strong = weak->data()) {
|
||||
if (const auto strong = weak->get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
@@ -458,14 +458,14 @@ void SuggestOfferForMessage(
|
||||
HistoryView::SuggestMode mode) {
|
||||
const auto id = item->fullId();
|
||||
const auto state = std::make_shared<SendSuggestState>();
|
||||
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto weak = std::make_shared<base::weak_qptr<Ui::BoxContent>>();
|
||||
const auto done = [=](SuggestPostOptions result) {
|
||||
const auto item = show->session().data().message(id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto close = [=] {
|
||||
if (const auto strong = weak->data()) {
|
||||
if (const auto strong = weak->get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -25,6 +26,32 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool Transcribes::isRated(not_null<HistoryItem*> item) const {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
return _session->settings().isTranscriptionRated(transcribeId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transcribes::rate(not_null<HistoryItem*> item, bool isGood) {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
_api.request(MTPmessages_RateTranscribedAudio(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_long(transcribeId),
|
||||
MTP_bool(isGood))).send();
|
||||
_session->settings().markTranscriptionAsRated(transcribeId);
|
||||
_session->saveSettings();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
|
||||
if (const auto channel = item->history()->peer->asMegagroup()) {
|
||||
const auto owner = &channel->owner();
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
void apply(const MTPDupdateTranscribedAudio &update);
|
||||
|
||||
[[nodiscard]] bool freeFor(not_null<HistoryItem*> item) const;
|
||||
[[nodiscard]] bool isRated(not_null<HistoryItem*> item) const;
|
||||
void rate(not_null<HistoryItem*> item, bool isGood);
|
||||
|
||||
[[nodiscard]] bool trialsSupport();
|
||||
[[nodiscard]] TimeId trialsRefreshAt();
|
||||
|
||||
@@ -2221,6 +2221,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateNewAuthorization: {
|
||||
session().api().authorizations().apply(update);
|
||||
} break;
|
||||
|
||||
case mtpc_updateServiceNotification: {
|
||||
const auto &d = update.c_updateServiceNotification();
|
||||
const auto text = TextWithEntities {
|
||||
|
||||
@@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
// AyuGram includes
|
||||
#include "ayu/ayu_settings.h"
|
||||
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
@@ -83,6 +87,8 @@ void ViewsManager::pollExtendedMedia(
|
||||
}
|
||||
|
||||
void ViewsManager::viewsIncrement() {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
|
||||
for (auto i = _toIncrement.begin(); i != _toIncrement.cend();) {
|
||||
if (_incrementRequests.contains(i->first)) {
|
||||
++i;
|
||||
@@ -97,7 +103,7 @@ void ViewsManager::viewsIncrement() {
|
||||
const auto requestId = _api.request(MTPmessages_GetMessagesViews(
|
||||
i->first->input,
|
||||
MTP_vector<MTPint>(ids),
|
||||
MTP_bool(true)
|
||||
MTP_bool(settings.sendReadMessages)
|
||||
)).done([=](
|
||||
const MTPmessages_MessageViews &result,
|
||||
mtpRequestId requestId) {
|
||||
|
||||
@@ -240,13 +240,13 @@ struct State {
|
||||
[[nodiscard]] rpl::producer<Peers> WhoReadIds(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
auto weak = base::make_weak(context);
|
||||
const auto session = &item->history()->session();
|
||||
return [=](auto consumer) {
|
||||
if (!weak) {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto context = PreparedContextAt(weak.data(), session);
|
||||
const auto context = PreparedContextAt(weak.get(), session);
|
||||
auto &entry = context->cacheRead(item);
|
||||
if (entry.requestId) {
|
||||
} else if (const auto user = item->history()->peer->asUser()) {
|
||||
@@ -325,13 +325,13 @@ struct State {
|
||||
not_null<HistoryItem*> item,
|
||||
const ReactionId &reaction,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
auto weak = base::make_weak(context);
|
||||
const auto session = &item->history()->session();
|
||||
return [=](auto consumer) {
|
||||
if (!weak) {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto context = PreparedContextAt(weak.data(), session);
|
||||
const auto context = PreparedContextAt(weak.get(), session);
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
if (!entry.requestId) {
|
||||
using Flag = MTPmessages_GetMessageReactionsList::Flag;
|
||||
@@ -652,6 +652,22 @@ QString FormatReadDate(TimeId date, const QDateTime &now) {
|
||||
const auto parsed = base::unixtime::parse(date);
|
||||
const auto readDate = parsed.date();
|
||||
const auto nowDate = now.date();
|
||||
|
||||
if (readDate.year() < nowDate.year()) {
|
||||
return tr::lng_mediaview_date_time(
|
||||
tr::now,
|
||||
lt_date,
|
||||
tr::lng_month_day_year(
|
||||
tr::now,
|
||||
lt_month,
|
||||
Lang::MonthDay(readDate.month())(tr::now),
|
||||
lt_day,
|
||||
QString::number(readDate.day()),
|
||||
lt_year,
|
||||
QString::number(readDate.year())),
|
||||
lt_time,
|
||||
QLocale().toString(parsed.time(), "HH:mm:ss"));
|
||||
}
|
||||
if (readDate == nowDate) {
|
||||
return tr::lng_mediaview_today(
|
||||
tr::now,
|
||||
|
||||
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -2497,7 +2498,17 @@ void ApiWrap::refreshFileReference(
|
||||
v::match(origin.data, [&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
const auto media = item->media();
|
||||
const auto storyId = media ? media->storyId() : FullStoryId();
|
||||
const auto mediaStory = media ? media->storyId() : FullStoryId();
|
||||
const auto storyId = mediaStory
|
||||
? mediaStory
|
||||
: FullStoryId{
|
||||
(IsStoryMsgId(item->id)
|
||||
? item->history()->peer->id
|
||||
: PeerId()),
|
||||
(IsStoryMsgId(item->id)
|
||||
? StoryIdFromMsgId(item->id)
|
||||
: StoryId())
|
||||
};
|
||||
if (storyId) {
|
||||
request(MTPstories_GetStoriesByID(
|
||||
_session->data().peer(storyId.peer)->input,
|
||||
@@ -2508,6 +2519,17 @@ void ApiWrap::refreshFileReference(
|
||||
request(MTPmessages_GetScheduledMessages(
|
||||
item->history()->peer->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(realId))));
|
||||
} else if (item->isSavedMusicItem()) {
|
||||
const auto user = item->history()->peer->asUser();
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
if (user && document) {
|
||||
request(MTPusers_GetSavedMusicByID(
|
||||
user->inputUser,
|
||||
MTP_vector<MTPInputDocument>(1, document->mtpInput())));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
} else if (item->isBusinessShortcut()) {
|
||||
const auto &shortcuts = _session->data().shortcutMessages();
|
||||
const auto realId = shortcuts.lookupId(item);
|
||||
@@ -3379,8 +3401,8 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
return;
|
||||
}
|
||||
|
||||
forwardMessages(std::move(toForward), action);
|
||||
history->setForwardDraft(topicRootId, monoforumPeerId, {});
|
||||
forwardMessages(std::move(toForward), action);
|
||||
}
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -3417,6 +3439,22 @@ void ApiWrap::forwardMessages(
|
||||
|
||||
auto &histories = _session->data().histories();
|
||||
|
||||
for (auto i = begin(draft.items); i != end(draft.items);) {
|
||||
const auto item = *i;
|
||||
if (item->isSavedMusicItem()) {
|
||||
SendExistingDocument(MessageToSend(action), item->media()->document());
|
||||
i = draft.items.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (draft.items.empty()) {
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct SharedCallback {
|
||||
int requestsLeft = 0;
|
||||
FnMut<void()> callback;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
enum class DefaultNotify;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
enum class StickersType : uchar;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
@@ -428,6 +428,9 @@ public:
|
||||
|
||||
static constexpr auto kJoinErrorDuration = 5 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<TaskQueue> &fileLoader() {
|
||||
return _fileLoader;
|
||||
}
|
||||
private:
|
||||
struct MessageDataRequest {
|
||||
using Callbacks = std::vector<Fn<void()>>;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
|
||||
// https://github.com/AyuGram/AyuGram4AX/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/AyuConstants.java
|
||||
constexpr int DOCUMENT_TYPE_NONE = 0;
|
||||
constexpr int DOCUMENT_TYPE_PHOTO = 1;
|
||||
constexpr int DOCUMENT_TYPE_STICKER = 2;
|
||||
constexpr int DOCUMENT_TYPE_FILE = 3;
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "ayu/ayu_ui_settings.h"
|
||||
#include "ayu/ayu_worker.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "features/translator/ayu_translator.h"
|
||||
#include "features/filters/shadow_ban_utils.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "utils/rc_manager.h"
|
||||
|
||||
@@ -32,6 +34,7 @@ void initUiSettings() {
|
||||
|
||||
AyuUiSettings::setMonoFont(settings.monoFont);
|
||||
AyuUiSettings::setWideMultiplier(settings.wideMultiplier);
|
||||
AyuUiSettings::setMaterialSwitches(settings.materialSwitches);
|
||||
}
|
||||
|
||||
void initDatabase() {
|
||||
@@ -46,12 +49,17 @@ void initRCManager() {
|
||||
RCManager::getInstance().start();
|
||||
}
|
||||
|
||||
void initTranslator() {
|
||||
Ayu::Translator::TranslateManager::init();
|
||||
}
|
||||
|
||||
void init() {
|
||||
initLang();
|
||||
initDatabase();
|
||||
initUiSettings();
|
||||
initWorker();
|
||||
initRCManager();
|
||||
initTranslator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <fstream>
|
||||
|
||||
#include "ayu_worker.h"
|
||||
#include "features/translator/ayu_translator.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
@@ -41,7 +42,10 @@ rpl::variable<QString> editedMarkReactive;
|
||||
|
||||
rpl::variable<int> showPeerIdReactive;
|
||||
|
||||
rpl::variable<bool> hideFromBlockedReactive;
|
||||
rpl::variable<QString> translationProviderReactive;
|
||||
|
||||
rpl::event_stream<> filtersUpdateReactive; // triggered on adding / editing filter
|
||||
|
||||
rpl::event_stream<> historyUpdateReactive;
|
||||
|
||||
rpl::lifetime lifetime = rpl::lifetime();
|
||||
@@ -137,8 +141,7 @@ void postinitialize() {
|
||||
deletedMarkReactive = settings->deletedMark;
|
||||
editedMarkReactive = settings->editedMark;
|
||||
showPeerIdReactive = settings->showPeerId;
|
||||
|
||||
hideFromBlockedReactive = settings->hideFromBlocked;
|
||||
translationProviderReactive = settings->translationProvider;
|
||||
|
||||
ghostModeEnabled = ghostModeEnabled_util(settings.value());
|
||||
}
|
||||
@@ -194,6 +197,15 @@ void save() {
|
||||
postinitialize();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
lifetime.destroy();
|
||||
lifetime = rpl::lifetime();
|
||||
settings = std::nullopt;
|
||||
initialize();
|
||||
postinitialize();
|
||||
save();
|
||||
}
|
||||
|
||||
AyuGramSettings::AyuGramSettings() {
|
||||
// ~ Ghost essentials
|
||||
sendReadMessages = true;
|
||||
@@ -213,6 +225,8 @@ AyuGramSettings::AyuGramSettings() {
|
||||
saveForBots = false;
|
||||
|
||||
// ~ Message filters
|
||||
filtersEnabled = false;
|
||||
filtersEnabledInChats = false;
|
||||
hideFromBlocked = false;
|
||||
|
||||
// ~ QoL toggles
|
||||
@@ -229,6 +243,9 @@ AyuGramSettings::AyuGramSettings() {
|
||||
increaseWebviewHeight = false;
|
||||
increaseWebviewWidth = false;
|
||||
|
||||
materialSwitches = true;
|
||||
removeMessageTail = false;
|
||||
|
||||
disableNotificationsDelay = false;
|
||||
localPremium = false;
|
||||
showChannelReactions = true;
|
||||
@@ -241,8 +258,9 @@ AyuGramSettings::AyuGramSettings() {
|
||||
#else
|
||||
AyuAssets::DEFAULT_ICON
|
||||
#endif
|
||||
;
|
||||
simpleQuotesAndReplies = true;
|
||||
;
|
||||
simpleQuotesAndReplies = false;
|
||||
hideFastShare = false;
|
||||
replaceBottomInfoWithIcons = true;
|
||||
deletedMark = "🧹";
|
||||
editedMark = Core::IsAppLaunched() ? tr::lng_edited(tr::now) : QString("edited");
|
||||
@@ -257,6 +275,7 @@ AyuGramSettings::AyuGramSettings() {
|
||||
showHideMessageInContextMenu = 0;
|
||||
showUserMessagesInContextMenu = 2;
|
||||
showMessageDetailsInContextMenu = 2;
|
||||
showAddFilterInContextMenu = 1;
|
||||
|
||||
showAttachButtonInMessageField = true;
|
||||
showCommandsButtonInMessageField = true;
|
||||
@@ -267,8 +286,17 @@ AyuGramSettings::AyuGramSettings() {
|
||||
showAttachPopup = true;
|
||||
showEmojiPopup = true;
|
||||
|
||||
// ~ Drawer Elements
|
||||
showMyProfileInDrawer = true;
|
||||
showBotsInDrawer = true;
|
||||
showNewGroupInDrawer = true;
|
||||
showNewChannelInDrawer = true;
|
||||
showContactsInDrawer = true;
|
||||
showCallsInDrawer = true;
|
||||
showSavedMessagesInDrawer = true;
|
||||
showLReadToggleInDrawer = false;
|
||||
showSReadToggleInDrawer = true;
|
||||
showNightModeToggleInDrawer = true;
|
||||
showGhostToggleInDrawer = true;
|
||||
showStreamerToggleInDrawer = false;
|
||||
|
||||
@@ -287,6 +315,7 @@ AyuGramSettings::AyuGramSettings() {
|
||||
* channelBottomButton = 2 means "Discuss" + fallback to "Mute"/"Unmute"
|
||||
*/
|
||||
channelBottomButton = 2;
|
||||
quickAdminShortcuts = true;
|
||||
|
||||
/*
|
||||
* showPeerId = 0 means no ID shown
|
||||
@@ -301,6 +330,12 @@ AyuGramSettings::AyuGramSettings() {
|
||||
stickerConfirmation = false;
|
||||
gifConfirmation = false;
|
||||
voiceConfirmation = false;
|
||||
|
||||
translationProvider = "telegram"; // telegram, google, yandex
|
||||
|
||||
adaptiveCoverColor = true;
|
||||
|
||||
crashReporting = true;
|
||||
}
|
||||
|
||||
void set_sendReadMessages(bool val) {
|
||||
@@ -366,9 +401,16 @@ void set_saveForBots(bool val) {
|
||||
settings->saveForBots = val;
|
||||
}
|
||||
|
||||
void set_filtersEnabled(bool val) {
|
||||
settings->filtersEnabled = val;
|
||||
}
|
||||
|
||||
void set_filtersEnabledInChats(bool val) {
|
||||
settings->filtersEnabledInChats = val;
|
||||
}
|
||||
|
||||
void set_hideFromBlocked(bool val) {
|
||||
settings->hideFromBlocked = val;
|
||||
hideFromBlockedReactive = val;
|
||||
}
|
||||
|
||||
void set_disableAds(bool val) {
|
||||
@@ -411,6 +453,14 @@ void set_increaseWebviewWidth(bool val) {
|
||||
settings->increaseWebviewWidth = val;
|
||||
}
|
||||
|
||||
void set_materialSwitches(bool val) {
|
||||
settings->materialSwitches = val;
|
||||
}
|
||||
|
||||
void set_removeMessageTail(bool val) {
|
||||
settings->removeMessageTail = val;
|
||||
}
|
||||
|
||||
void set_disableNotificationsDelay(bool val) {
|
||||
settings->disableNotificationsDelay = val;
|
||||
}
|
||||
@@ -435,6 +485,10 @@ void set_simpleQuotesAndReplies(bool val) {
|
||||
settings->simpleQuotesAndReplies = val;
|
||||
}
|
||||
|
||||
void set_hideFastShare(bool val) {
|
||||
settings->hideFastShare = val;
|
||||
}
|
||||
|
||||
void set_replaceBottomInfoWithIcons(bool val) {
|
||||
settings->replaceBottomInfoWithIcons = val;
|
||||
}
|
||||
@@ -473,6 +527,10 @@ void set_showMessageDetailsInContextMenu(int val) {
|
||||
settings->showMessageDetailsInContextMenu = val;
|
||||
}
|
||||
|
||||
void set_showAddFilterInContextMenu(int val) {
|
||||
settings->showAddFilterInContextMenu = val;
|
||||
}
|
||||
|
||||
void set_showAttachButtonInMessageField(bool val) {
|
||||
settings->showAttachButtonInMessageField = val;
|
||||
triggerHistoryUpdate();
|
||||
@@ -508,6 +566,34 @@ void set_showEmojiPopup(bool val) {
|
||||
triggerHistoryUpdate();
|
||||
}
|
||||
|
||||
void set_showMyProfileInDrawer(bool val) {
|
||||
settings->showMyProfileInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showBotsInDrawer(bool val) {
|
||||
settings->showBotsInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showNewGroupInDrawer(bool val) {
|
||||
settings->showNewGroupInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showNewChannelInDrawer(bool val) {
|
||||
settings->showNewChannelInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showContactsInDrawer(bool val) {
|
||||
settings->showContactsInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showCallsInDrawer(bool val) {
|
||||
settings->showCallsInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showSavedMessagesInDrawer(bool val) {
|
||||
settings->showSavedMessagesInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showLReadToggleInDrawer(bool val) {
|
||||
settings->showLReadToggleInDrawer = val;
|
||||
}
|
||||
@@ -516,6 +602,10 @@ void set_showSReadToggleInDrawer(bool val) {
|
||||
settings->showSReadToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showNightModeToggleInDrawer(bool val) {
|
||||
settings->showNightModeToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void set_showGhostToggleInDrawer(bool val) {
|
||||
settings->showGhostToggleInDrawer = val;
|
||||
}
|
||||
@@ -557,6 +647,10 @@ void set_channelBottomButton(int val) {
|
||||
settings->channelBottomButton = val;
|
||||
}
|
||||
|
||||
void set_quickAdminShortcuts(bool val) {
|
||||
settings->quickAdminShortcuts = val;
|
||||
}
|
||||
|
||||
void set_showMessageSeconds(bool val) {
|
||||
settings->showMessageSeconds = val;
|
||||
}
|
||||
@@ -577,6 +671,20 @@ void set_voiceConfirmation(bool val) {
|
||||
settings->voiceConfirmation = val;
|
||||
}
|
||||
|
||||
void set_translationProvider(const QString &val) {
|
||||
settings->translationProvider = val;
|
||||
translationProviderReactive = val;
|
||||
Ayu::Translator::TranslateManager::currentInstance()->resetCache();
|
||||
}
|
||||
|
||||
void set_adaptiveCoverColor(bool val) {
|
||||
settings->adaptiveCoverColor = val;
|
||||
}
|
||||
|
||||
void set_crashReporting(bool val) {
|
||||
settings->crashReporting = val;
|
||||
}
|
||||
|
||||
bool isUseScheduledMessages() {
|
||||
return isGhostModeActive() && settings->useScheduledMessages;
|
||||
}
|
||||
@@ -597,12 +705,20 @@ rpl::producer<int> get_showPeerIdReactive() {
|
||||
return showPeerIdReactive.value();
|
||||
}
|
||||
|
||||
rpl::producer<QString> get_translationProviderReactive() {
|
||||
return translationProviderReactive.value();
|
||||
}
|
||||
|
||||
rpl::producer<bool> get_ghostModeEnabledReactive() {
|
||||
return ghostModeEnabled.value();
|
||||
}
|
||||
|
||||
rpl::producer<bool> get_hideFromBlockedReactive() {
|
||||
return hideFromBlockedReactive.value();
|
||||
void fire_filtersUpdate() {
|
||||
filtersUpdateReactive.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> get_filtersUpdate() {
|
||||
return filtersUpdateReactive.events();
|
||||
}
|
||||
|
||||
void triggerHistoryUpdate() {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include "ayu/libs/json.hpp"
|
||||
#include "ayu/libs/json_ext.hpp"
|
||||
|
||||
@@ -59,6 +60,9 @@ public:
|
||||
|
||||
bool saveForBots;
|
||||
|
||||
std::unordered_set<long long> shadowBanIds;
|
||||
bool filtersEnabled;
|
||||
bool filtersEnabledInChats;
|
||||
bool hideFromBlocked;
|
||||
|
||||
bool disableAds;
|
||||
@@ -74,6 +78,9 @@ public:
|
||||
bool increaseWebviewHeight;
|
||||
bool increaseWebviewWidth;
|
||||
|
||||
bool materialSwitches;
|
||||
bool removeMessageTail;
|
||||
|
||||
bool disableNotificationsDelay;
|
||||
bool localPremium;
|
||||
bool showChannelReactions;
|
||||
@@ -81,6 +88,7 @@ public:
|
||||
|
||||
QString appIcon;
|
||||
bool simpleQuotesAndReplies;
|
||||
bool hideFastShare;
|
||||
bool replaceBottomInfoWithIcons;
|
||||
QString deletedMark;
|
||||
QString editedMark;
|
||||
@@ -91,6 +99,7 @@ public:
|
||||
int showHideMessageInContextMenu;
|
||||
int showUserMessagesInContextMenu;
|
||||
int showMessageDetailsInContextMenu;
|
||||
int showAddFilterInContextMenu;
|
||||
|
||||
bool showAttachButtonInMessageField;
|
||||
bool showCommandsButtonInMessageField;
|
||||
@@ -101,8 +110,16 @@ public:
|
||||
bool showAttachPopup;
|
||||
bool showEmojiPopup;
|
||||
|
||||
bool showMyProfileInDrawer;
|
||||
bool showBotsInDrawer;
|
||||
bool showNewGroupInDrawer;
|
||||
bool showNewChannelInDrawer;
|
||||
bool showContactsInDrawer;
|
||||
bool showCallsInDrawer;
|
||||
bool showSavedMessagesInDrawer;
|
||||
bool showLReadToggleInDrawer;
|
||||
bool showSReadToggleInDrawer;
|
||||
bool showNightModeToggleInDrawer;
|
||||
bool showGhostToggleInDrawer;
|
||||
bool showStreamerToggleInDrawer;
|
||||
|
||||
@@ -116,6 +133,7 @@ public:
|
||||
bool hideAllChatsFolder;
|
||||
|
||||
int channelBottomButton;
|
||||
bool quickAdminShortcuts;
|
||||
|
||||
int showPeerId;
|
||||
bool showMessageSeconds;
|
||||
@@ -124,6 +142,12 @@ public:
|
||||
bool stickerConfirmation;
|
||||
bool gifConfirmation;
|
||||
bool voiceConfirmation;
|
||||
|
||||
QString translationProvider;
|
||||
|
||||
bool adaptiveCoverColor;
|
||||
|
||||
bool crashReporting;
|
||||
};
|
||||
|
||||
void set_sendReadMessages(bool val);
|
||||
@@ -143,6 +167,8 @@ void set_saveMessagesHistory(bool val);
|
||||
|
||||
void set_saveForBots(bool val);
|
||||
|
||||
void set_filtersEnabled(bool val);
|
||||
void set_filtersEnabledInChats(bool val);
|
||||
void set_hideFromBlocked(bool val);
|
||||
|
||||
void set_disableAds(bool val);
|
||||
@@ -158,6 +184,9 @@ void set_spoofWebviewAsAndroid(bool val);
|
||||
void set_increaseWebviewHeight(bool val);
|
||||
void set_increaseWebviewWidth(bool val);
|
||||
|
||||
void set_materialSwitches(bool val);
|
||||
void set_removeMessageTail(bool val);
|
||||
|
||||
void set_disableNotificationsDelay(bool val);
|
||||
void set_localPremium(bool val);
|
||||
void set_hideChannelReactions(bool val);
|
||||
@@ -165,6 +194,7 @@ void set_hideGroupReactions(bool val);
|
||||
|
||||
void set_appIcon(const QString &val);
|
||||
void set_simpleQuotesAndReplies(bool val);
|
||||
void set_hideFastShare(bool val);
|
||||
void set_replaceBottomInfoWithIcons(bool val);
|
||||
void set_deletedMark(const QString &val);
|
||||
void set_editedMark(const QString &val);
|
||||
@@ -175,6 +205,7 @@ void set_showViewsPanelInContextMenu(int val);
|
||||
void set_showHideMessageInContextMenu(int val);
|
||||
void set_showUserMessagesInContextMenu(int val);
|
||||
void set_showMessageDetailsInContextMenu(int val);
|
||||
void set_showAddFilterInContextMenu(int val);
|
||||
|
||||
void set_showAttachButtonInMessageField(bool val);
|
||||
void set_showCommandsButtonInMessageField(bool val);
|
||||
@@ -185,8 +216,16 @@ void set_showAutoDeleteButtonInMessageField(bool val);
|
||||
void set_showAttachPopup(bool val);
|
||||
void set_showEmojiPopup(bool val);
|
||||
|
||||
void set_showMyProfileInDrawer(bool val);
|
||||
void set_showBotsInDrawer(bool val);
|
||||
void set_showNewGroupInDrawer(bool val);
|
||||
void set_showNewChannelInDrawer(bool val);
|
||||
void set_showContactsInDrawer(bool val);
|
||||
void set_showCallsInDrawer(bool val);
|
||||
void set_showSavedMessagesInDrawer(bool val);
|
||||
void set_showLReadToggleInDrawer(bool val);
|
||||
void set_showSReadToggleInDrawer(bool val);
|
||||
void set_showNightModeToggleInDrawer(bool val);
|
||||
void set_showGhostToggleInDrawer(bool val);
|
||||
void set_showStreamerToggleInDrawer(bool val);
|
||||
|
||||
@@ -200,6 +239,7 @@ void set_hideNotificationBadge(bool val);
|
||||
void set_hideAllChatsFolder(bool val);
|
||||
|
||||
void set_channelBottomButton(int val);
|
||||
void set_quickAdminShortcuts(bool val);
|
||||
|
||||
void set_showPeerId(int val);
|
||||
void set_showMessageSeconds(bool val);
|
||||
@@ -209,6 +249,12 @@ void set_stickerConfirmation(bool val);
|
||||
void set_gifConfirmation(bool val);
|
||||
void set_voiceConfirmation(bool val);
|
||||
|
||||
void set_translationProvider(const QString &val);
|
||||
|
||||
void set_adaptiveCoverColor(bool val);
|
||||
|
||||
void set_crashReporting(bool val);
|
||||
|
||||
inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nlohmann_json_t) {
|
||||
NLOHMANN_JSON_TO(sendReadMessages)
|
||||
NLOHMANN_JSON_TO(sendReadStories)
|
||||
@@ -221,6 +267,9 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
|
||||
NLOHMANN_JSON_TO(saveDeletedMessages)
|
||||
NLOHMANN_JSON_TO(saveMessagesHistory)
|
||||
NLOHMANN_JSON_TO(saveForBots)
|
||||
NLOHMANN_JSON_TO(shadowBanIds)
|
||||
NLOHMANN_JSON_TO(filtersEnabled)
|
||||
NLOHMANN_JSON_TO(filtersEnabledInChats)
|
||||
NLOHMANN_JSON_TO(hideFromBlocked)
|
||||
NLOHMANN_JSON_TO(disableAds)
|
||||
NLOHMANN_JSON_TO(disableStories)
|
||||
@@ -232,10 +281,13 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
|
||||
NLOHMANN_JSON_TO(spoofWebviewAsAndroid)
|
||||
NLOHMANN_JSON_TO(increaseWebviewHeight)
|
||||
NLOHMANN_JSON_TO(increaseWebviewWidth)
|
||||
NLOHMANN_JSON_TO(materialSwitches)
|
||||
NLOHMANN_JSON_TO(removeMessageTail)
|
||||
NLOHMANN_JSON_TO(disableNotificationsDelay)
|
||||
NLOHMANN_JSON_TO(localPremium)
|
||||
NLOHMANN_JSON_TO(appIcon)
|
||||
NLOHMANN_JSON_TO(simpleQuotesAndReplies)
|
||||
NLOHMANN_JSON_TO(hideFastShare)
|
||||
NLOHMANN_JSON_TO(replaceBottomInfoWithIcons)
|
||||
NLOHMANN_JSON_TO(deletedMark)
|
||||
NLOHMANN_JSON_TO(editedMark)
|
||||
@@ -245,6 +297,7 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
|
||||
NLOHMANN_JSON_TO(showHideMessageInContextMenu)
|
||||
NLOHMANN_JSON_TO(showUserMessagesInContextMenu)
|
||||
NLOHMANN_JSON_TO(showMessageDetailsInContextMenu)
|
||||
NLOHMANN_JSON_TO(showAddFilterInContextMenu)
|
||||
NLOHMANN_JSON_TO(showAttachButtonInMessageField)
|
||||
NLOHMANN_JSON_TO(showCommandsButtonInMessageField)
|
||||
NLOHMANN_JSON_TO(showEmojiButtonInMessageField)
|
||||
@@ -252,8 +305,16 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
|
||||
NLOHMANN_JSON_TO(showAutoDeleteButtonInMessageField)
|
||||
NLOHMANN_JSON_TO(showAttachPopup)
|
||||
NLOHMANN_JSON_TO(showEmojiPopup)
|
||||
NLOHMANN_JSON_TO(showMyProfileInDrawer)
|
||||
NLOHMANN_JSON_TO(showBotsInDrawer)
|
||||
NLOHMANN_JSON_TO(showNewGroupInDrawer)
|
||||
NLOHMANN_JSON_TO(showNewChannelInDrawer)
|
||||
NLOHMANN_JSON_TO(showContactsInDrawer)
|
||||
NLOHMANN_JSON_TO(showCallsInDrawer)
|
||||
NLOHMANN_JSON_TO(showSavedMessagesInDrawer)
|
||||
NLOHMANN_JSON_TO(showLReadToggleInDrawer)
|
||||
NLOHMANN_JSON_TO(showSReadToggleInDrawer)
|
||||
NLOHMANN_JSON_TO(showNightModeToggleInDrawer)
|
||||
NLOHMANN_JSON_TO(showGhostToggleInDrawer)
|
||||
NLOHMANN_JSON_TO(showStreamerToggleInDrawer)
|
||||
NLOHMANN_JSON_TO(showGhostToggleInTray)
|
||||
@@ -265,12 +326,16 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
|
||||
NLOHMANN_JSON_TO(hideNotificationBadge)
|
||||
NLOHMANN_JSON_TO(hideAllChatsFolder)
|
||||
NLOHMANN_JSON_TO(channelBottomButton)
|
||||
NLOHMANN_JSON_TO(quickAdminShortcuts)
|
||||
NLOHMANN_JSON_TO(showPeerId)
|
||||
NLOHMANN_JSON_TO(showMessageSeconds)
|
||||
NLOHMANN_JSON_TO(showMessageShot)
|
||||
NLOHMANN_JSON_TO(stickerConfirmation)
|
||||
NLOHMANN_JSON_TO(gifConfirmation)
|
||||
NLOHMANN_JSON_TO(voiceConfirmation)
|
||||
NLOHMANN_JSON_TO(translationProvider)
|
||||
NLOHMANN_JSON_TO(adaptiveCoverColor)
|
||||
NLOHMANN_JSON_TO(crashReporting)
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nlohmann_json_t) {
|
||||
@@ -286,6 +351,9 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveDeletedMessages)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveMessagesHistory)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(saveForBots)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(filtersEnabled)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(filtersEnabledInChats)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(shadowBanIds)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideFromBlocked)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableAds)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableStories)
|
||||
@@ -297,10 +365,13 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(spoofWebviewAsAndroid)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewHeight)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewWidth)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(materialSwitches)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(removeMessageTail)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableNotificationsDelay)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(localPremium)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(appIcon)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(simpleQuotesAndReplies)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideFastShare)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(replaceBottomInfoWithIcons)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(deletedMark)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(editedMark)
|
||||
@@ -310,6 +381,7 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showHideMessageInContextMenu)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showUserMessagesInContextMenu)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageDetailsInContextMenu)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAddFilterInContextMenu)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAttachButtonInMessageField)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showCommandsButtonInMessageField)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showEmojiButtonInMessageField)
|
||||
@@ -317,8 +389,16 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAutoDeleteButtonInMessageField)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showAttachPopup)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showEmojiPopup)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMyProfileInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showBotsInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showNewGroupInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showNewChannelInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showContactsInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showCallsInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showSavedMessagesInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showLReadToggleInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showSReadToggleInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showNightModeToggleInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showGhostToggleInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showStreamerToggleInDrawer)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showGhostToggleInTray)
|
||||
@@ -330,30 +410,38 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideNotificationBadge)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(hideAllChatsFolder)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(channelBottomButton)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(quickAdminShortcuts)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showPeerId)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageSeconds)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(showMessageShot)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(stickerConfirmation)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(gifConfirmation)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(voiceConfirmation)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(translationProvider)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(adaptiveCoverColor)
|
||||
NLOHMANN_JSON_FROM_WITH_DEFAULT(crashReporting)
|
||||
}
|
||||
|
||||
AyuGramSettings &getInstance();
|
||||
|
||||
void load();
|
||||
void save();
|
||||
void reset();
|
||||
|
||||
rpl::producer<QString> get_deletedMarkReactive();
|
||||
rpl::producer<QString> get_editedMarkReactive();
|
||||
|
||||
rpl::producer<int> get_showPeerIdReactive();
|
||||
|
||||
rpl::producer<QString> get_translationProviderReactive();
|
||||
|
||||
bool isGhostModeActive();
|
||||
bool isUseScheduledMessages();
|
||||
|
||||
rpl::producer<bool> get_ghostModeEnabledReactive();
|
||||
|
||||
rpl::producer<bool> get_hideFromBlockedReactive();
|
||||
void fire_filtersUpdate();
|
||||
rpl::producer<> get_filtersUpdate();
|
||||
|
||||
void triggerHistoryUpdate();
|
||||
rpl::producer<> get_historyUpdateReactive();
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/donate_info_box.h"
|
||||
#include "ui/settings/settings_main.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
namespace AyuUrlHandlers {
|
||||
|
||||
@@ -43,10 +46,48 @@ bool ResolveUser(
|
||||
return true;
|
||||
}
|
||||
|
||||
searchById(
|
||||
searchUserById(
|
||||
userId,
|
||||
&controller->session(),
|
||||
[=](const QString &title, UserData *data)
|
||||
[=](const QString &title, PeerData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
return;
|
||||
}
|
||||
|
||||
Core::App().hideMediaView();
|
||||
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResolveChat(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto params = url_parse_params(
|
||||
match->captured(1),
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
const auto chatId = params.value(qsl("id")).toLongLong();
|
||||
if (!chatId) {
|
||||
return false;
|
||||
}
|
||||
const auto peer = controller->session().data().peerLoaded(static_cast<PeerId>(chatId));
|
||||
if (peer != nullptr) {
|
||||
controller->showPeerInfo(peer);
|
||||
return true;
|
||||
}
|
||||
|
||||
searchChatById(
|
||||
chatId,
|
||||
&controller->session(),
|
||||
[=](const QString &title, PeerData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
@@ -68,7 +109,40 @@ bool HandleAyu(
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
controller->showToast(QString(":3"), 500);
|
||||
|
||||
try {
|
||||
const auto section = match->captured(1).mid(1).toLower();
|
||||
const auto type = [&]() -> std::optional<::Settings::Type>
|
||||
{
|
||||
if (section == u"settings"_q || section == u"preferences"_q || section == u"prefs"_q) {
|
||||
return ::Settings::AyuMain::Id();
|
||||
}
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
if (type.has_value()) {
|
||||
controller->showSettings(*type);
|
||||
controller->window().activate();
|
||||
} else {
|
||||
controller->showToast(QString(":3"), 500);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleSupport(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
auto box = Box(
|
||||
Ui::FillDonateInfoBox,
|
||||
controller);
|
||||
Ui::show(std::move(box));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,21 @@ bool ResolveUser(
|
||||
const Match &match,
|
||||
const QVariant &context);
|
||||
|
||||
bool ResolveChat(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context);
|
||||
|
||||
bool HandleAyu(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context);
|
||||
|
||||
bool HandleSupport(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context);
|
||||
|
||||
bool TryHandleSpotify(const QString &url);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
using namespace sqlite_orm;
|
||||
auto storage = make_storage(
|
||||
"./tdata/ayudata.db",
|
||||
make_table<SchemaVersion>(
|
||||
"SchemaVersion",
|
||||
make_column("id", &SchemaVersion::id, primary_key()),
|
||||
make_column("version", &SchemaVersion::version)
|
||||
),
|
||||
make_index("idx_deleted_message_userId_dialogId_topicId_messageId",
|
||||
column<DeletedMessage>(&DeletedMessage::userId),
|
||||
column<DeletedMessage>(&DeletedMessage::dialogId),
|
||||
@@ -111,7 +116,7 @@ auto storage = make_storage(
|
||||
),
|
||||
make_table<RegexFilter>(
|
||||
"RegexFilter",
|
||||
make_column("id", &RegexFilter::id),
|
||||
make_column("id", &RegexFilter::id, primary_key()),
|
||||
make_column("text", &RegexFilter::text),
|
||||
make_column("enabled", &RegexFilter::enabled),
|
||||
make_column("reversed", &RegexFilter::reversed),
|
||||
@@ -142,6 +147,68 @@ auto storage = make_storage(
|
||||
)
|
||||
);
|
||||
|
||||
namespace AyuMigrations {
|
||||
|
||||
void migrateToV1(decltype(storage) &storage) {
|
||||
// drop RegexFilter table as we've added primary_key()
|
||||
try {
|
||||
storage.drop_table_if_exists("RegexFilter");
|
||||
LOG(("Migration to V1 successful."));
|
||||
} catch (const std::exception &ex) {
|
||||
LOG(("Migration to V1 failed: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void runMigrations(decltype(storage) &storage) {
|
||||
constexpr int kLatestVersion = 1;
|
||||
|
||||
const std::map<int, Fn<void(decltype(storage) &)>> migrations = {
|
||||
{1, AyuMigrations::migrateToV1},
|
||||
};
|
||||
|
||||
int currentVersion = 0;
|
||||
try {
|
||||
if (auto versionRow = storage.get_pointer<SchemaVersion>(1)) {
|
||||
currentVersion = versionRow->version;
|
||||
} else {
|
||||
storage.insert(SchemaVersion{1, 0});
|
||||
}
|
||||
} catch (...) {
|
||||
LOG(("No SchemaVersion, assuming 0"));
|
||||
storage.insert(SchemaVersion{1, 0});
|
||||
}
|
||||
|
||||
if (currentVersion >= kLatestVersion) {
|
||||
LOG(("Database is ok"));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("Database version: %1. Latest version: %2.").arg(currentVersion).arg(kLatestVersion));
|
||||
|
||||
for (int v = currentVersion + 1; v <= kLatestVersion; ++v) {
|
||||
if (migrations.contains(v)) {
|
||||
try {
|
||||
LOG(("Migration for version: %1").arg(v));
|
||||
storage.begin_transaction();
|
||||
|
||||
migrations.at(v)(storage);
|
||||
|
||||
storage.update_all(set(c(&SchemaVersion::version) = v), where(c(&SchemaVersion::id) == 1));
|
||||
storage.commit();
|
||||
LOG(("Applied migration for version: %1.").arg(v));
|
||||
} catch (...) {
|
||||
storage.rollback();
|
||||
LOG(("Failed to apply migration for version: %1.").arg(v));
|
||||
AyuDatabase::moveCurrentDatabase();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace AyuDatabase {
|
||||
|
||||
void moveCurrentDatabase() {
|
||||
@@ -161,38 +228,21 @@ void moveCurrentDatabase() {
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
auto movePrevious = false;
|
||||
|
||||
try {
|
||||
const auto res = storage.sync_schema_simulate(true);
|
||||
for (const auto val : res | std::views::values) {
|
||||
if (val == sync_schema_result::dropped_and_recreated) {
|
||||
movePrevious = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
LOG(("Exception during sync simulation; possibly corrupted database"));
|
||||
movePrevious = true;
|
||||
}
|
||||
|
||||
if (movePrevious) {
|
||||
moveCurrentDatabase();
|
||||
}
|
||||
|
||||
try {
|
||||
storage.sync_schema(true);
|
||||
} catch (...) {
|
||||
LOG(("Failed to sync database schema"));
|
||||
LOG(("Moving current database just in case"));
|
||||
|
||||
runMigrations(storage);
|
||||
|
||||
storage.sync_schema(true);
|
||||
} catch (const std::exception &ex) {
|
||||
LOG(("Database initialization failed: %1").arg(ex.what()));
|
||||
moveCurrentDatabase();
|
||||
|
||||
storage.sync_schema();
|
||||
storage.sync_schema(true);
|
||||
if (!storage.get_pointer<SchemaVersion>(1)) {
|
||||
storage.insert(SchemaVersion{1, 0});
|
||||
}
|
||||
}
|
||||
|
||||
storage.begin_transaction();
|
||||
storage.commit();
|
||||
}
|
||||
|
||||
void addEditedMessage(const EditedMessage &message) {
|
||||
@@ -277,4 +327,195 @@ bool hasDeletedMessages(ID userId, ID dialogId, ID topicId) {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> getAllT() {
|
||||
try {
|
||||
return storage.get_all<T>();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get all: %1").arg(ex.what()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RegexFilter> getAllRegexFilters() {
|
||||
return getAllT<RegexFilter>();
|
||||
}
|
||||
|
||||
std::vector<RegexFilterGlobalExclusion> getAllFiltersExclusions() {
|
||||
return getAllT<RegexFilterGlobalExclusion>();
|
||||
}
|
||||
|
||||
std::vector<RegexFilter> getExcludedByDialogId(ID dialogId) {
|
||||
try {
|
||||
return storage.get_all<RegexFilter>(
|
||||
where(in(&RegexFilter::id,
|
||||
storage.select(columns(&RegexFilterGlobalExclusion::filterId),
|
||||
where(is_equal(&RegexFilterGlobalExclusion::dialogId, dialogId))
|
||||
)
|
||||
))
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get excluded by dialog id: %1").arg(ex.what()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int getCount() {
|
||||
try {
|
||||
return storage.count<RegexFilter>();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get count: %1").arg(ex.what()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
RegexFilter getById(std::vector<char> id) {
|
||||
try {
|
||||
return storage.get<RegexFilter>(
|
||||
where(column<RegexFilter>(&RegexFilter::id) == std::move(id))
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get filters by id: %1").arg(ex.what()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RegexFilter> getShared() {
|
||||
try {
|
||||
return storage.get_all<RegexFilter>(
|
||||
where(is_null(column<RegexFilter>(&RegexFilter::dialogId)))
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get shared filters: %1").arg(ex.what()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RegexFilter> getByDialogId(ID dialogId) {
|
||||
try {
|
||||
return storage.get_all<RegexFilter>(
|
||||
where(column<RegexFilter>(&RegexFilter::dialogId) == dialogId)
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to get filters by dialog id: %1").arg(ex.what()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void addRegexFilter(const RegexFilter &filter) {
|
||||
try {
|
||||
storage.begin_transaction();
|
||||
storage.replace(filter); // we're using replace as we set std::vector<char> as primary key
|
||||
storage.commit();
|
||||
} catch (std::exception &ex) {
|
||||
storage.rollback();
|
||||
LOG(("Failed to save regex filter for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void addRegexExclusion(const RegexFilterGlobalExclusion &exclusion) {
|
||||
try {
|
||||
storage.begin_transaction();
|
||||
storage.insert(exclusion);
|
||||
storage.commit();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to save regex filter exclusion for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void updateRegexFilter(const RegexFilter &filter) {
|
||||
try {
|
||||
storage.update_all(
|
||||
set(
|
||||
c(&RegexFilter::text) = filter.text,
|
||||
c(&RegexFilter::enabled) = filter.enabled,
|
||||
c(&RegexFilter::reversed) = filter.reversed,
|
||||
c(&RegexFilter::caseInsensitive) = filter.caseInsensitive,
|
||||
c(&RegexFilter::dialogId) = filter.dialogId
|
||||
),
|
||||
where(c(&RegexFilter::id) == filter.id)
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to update regex filter for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFilter(const std::vector<char> &id) {
|
||||
try {
|
||||
storage.remove_all<RegexFilter>(
|
||||
where(column<RegexFilter>(&RegexFilter::id) == id)
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to delete regex filter for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void deleteExclusionsByFilterId(const std::vector<char> &id) {
|
||||
try {
|
||||
storage.remove_all<RegexFilterGlobalExclusion>(
|
||||
where(column<RegexFilterGlobalExclusion>(&RegexFilterGlobalExclusion::filterId) == id)
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to delete regex filter exclusion by filter id for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void deleteExclusion(ID dialogId, std::vector<char> filterId) {
|
||||
try {
|
||||
storage.remove_all<RegexFilterGlobalExclusion>(
|
||||
where(column<RegexFilterGlobalExclusion>(&RegexFilterGlobalExclusion::filterId) == filterId and
|
||||
column<RegexFilterGlobalExclusion>(&RegexFilterGlobalExclusion::dialogId) == dialogId
|
||||
)
|
||||
);
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to delete regex filter exclusion for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAllFilters() {
|
||||
try {
|
||||
storage.remove_all<RegexFilter>();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to delete all regex filter for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAllExclusions() {
|
||||
try {
|
||||
storage.remove_all<RegexFilterGlobalExclusion>();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to delete all regex filter exclusions for some reason: %1").arg(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasFilters() {
|
||||
try {
|
||||
return !storage.select(
|
||||
columns(column<RegexFilter>(&RegexFilter::id)),
|
||||
limit(1)
|
||||
).empty();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to check if there's any filters: %1").arg(ex.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasPerDialogFilters() {
|
||||
try {
|
||||
return
|
||||
!storage.select(
|
||||
columns(column<RegexFilter>(&RegexFilter::id)),
|
||||
where(is_not_null(column<RegexFilter>(&RegexFilter::dialogId))),
|
||||
limit(1)
|
||||
).empty() ||
|
||||
!storage.select(
|
||||
columns(column<RegexFilterGlobalExclusion>(&RegexFilterGlobalExclusion::fakeId)),
|
||||
limit(1)
|
||||
).empty();
|
||||
} catch (std::exception &ex) {
|
||||
LOG(("Failed to check if there's any filters: %1").arg(ex.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "entities.h"
|
||||
#include <functional>
|
||||
|
||||
|
||||
class SchemaVersion
|
||||
{
|
||||
public:
|
||||
int id;
|
||||
int version;
|
||||
};
|
||||
|
||||
namespace AyuDatabase {
|
||||
|
||||
@@ -20,4 +29,31 @@ void addDeletedMessage(const DeletedMessage &message);
|
||||
std::vector<DeletedMessage> getDeletedMessages(ID userId, ID dialogId, ID topicId, ID minId, ID maxId, int totalLimit);
|
||||
bool hasDeletedMessages(ID userId, ID dialogId, ID topicId);
|
||||
|
||||
std::vector<RegexFilter> getAllRegexFilters();
|
||||
RegexFilter getById(std::vector<char> id);
|
||||
std::vector<RegexFilter> getShared();
|
||||
std::vector<RegexFilter> getByDialogId(ID dialogId);
|
||||
std::vector<RegexFilterGlobalExclusion> getAllFiltersExclusions();
|
||||
std::vector<RegexFilter> getExcludedByDialogId(ID dialogId);
|
||||
|
||||
int getCount();
|
||||
|
||||
|
||||
void addRegexFilter(const RegexFilter &filter);
|
||||
void addRegexExclusion(const RegexFilterGlobalExclusion &exclusion);
|
||||
|
||||
void updateRegexFilter(const RegexFilter &filter);
|
||||
|
||||
void deleteFilter(const std::vector<char> &id);
|
||||
void deleteExclusionsByFilterId(const std::vector<char> &id);
|
||||
void deleteExclusion(ID dialogId, std::vector<char> filterId);
|
||||
|
||||
void deleteAllFilters();
|
||||
void deleteAllExclusions();
|
||||
|
||||
bool hasFilters();
|
||||
bool hasPerDialogFilters();
|
||||
|
||||
void moveCurrentDatabase();
|
||||
|
||||
}
|
||||
|
||||
@@ -80,7 +80,28 @@ public:
|
||||
bool enabled;
|
||||
bool reversed;
|
||||
bool caseInsensitive;
|
||||
std::unique_ptr<ID> dialogId; // nullable
|
||||
std::optional<ID> dialogId; // nullable
|
||||
|
||||
bool operator==(const RegexFilter &other) const {
|
||||
return id == other.id &&
|
||||
text == other.text &&
|
||||
caseInsensitive == other.caseInsensitive &&
|
||||
reversed == other.reversed &&
|
||||
dialogId == other.dialogId &&
|
||||
enabled == other.enabled;
|
||||
}
|
||||
[[nodiscard]] QJsonObject toJson() const {
|
||||
QJsonObject json;
|
||||
json["id"] = QString::fromUtf8(id.data());
|
||||
json["text"] = QString::fromStdString(text);
|
||||
json["enabled"] = enabled;
|
||||
json["reversed"] = reversed;
|
||||
json["caseInsensitive"] = caseInsensitive;
|
||||
if (dialogId.has_value()) {
|
||||
json["dialogId"] = dialogId.value();
|
||||
}
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
class RegexFilterGlobalExclusion
|
||||
@@ -89,6 +110,10 @@ public:
|
||||
ID fakeId;
|
||||
ID dialogId;
|
||||
std::vector<char> filterId;
|
||||
|
||||
bool operator==(const RegexFilterGlobalExclusion& other) const {
|
||||
return dialogId == other.dialogId && filterId == other.filterId;
|
||||
}
|
||||
};
|
||||
|
||||
class SpyMessageRead
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
// Copyright @Radolyn, 2025
|
||||
#include "ayu/data/messages_storage.h"
|
||||
|
||||
#include "ayu/ayu_constants.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "ayu/utils/ayu_mapper.h"
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
@@ -82,7 +81,7 @@ void map(not_null<HistoryItem*> item, AyuMessageBase &message) {
|
||||
// todo: implement mapping
|
||||
message.mediaPath = "/";
|
||||
// message.hqThumbPath
|
||||
message.documentType = DOCUMENT_TYPE_NONE;
|
||||
message.documentType = 0; // document type none
|
||||
// message.documentSerialized
|
||||
// message.thumbsSerialized
|
||||
// message.documentAttributesSerialized
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#include "filters_cache_controller.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "filters_controller.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
||||
static std::mutex mutex;
|
||||
|
||||
namespace FiltersCacheController {
|
||||
std::optional<std::vector<HashablePattern>> sharedPatterns;
|
||||
std::optional<std::unordered_map<long long, std::vector<ReversiblePattern>>> patternsByDialogId;
|
||||
|
||||
std::optional<std::unordered_map<long long, std::unordered_set<HashablePattern, PatternHasher>>> exclusionsByDialogId;
|
||||
|
||||
std::unordered_map<long long, std::unordered_map<std::optional<int>,std::optional<bool>>> filteredMessages;
|
||||
|
||||
|
||||
void rebuildCache() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
const auto filters = AyuDatabase::getAllRegexFilters();
|
||||
const auto exclusions = AyuDatabase::getAllFiltersExclusions();
|
||||
|
||||
std::vector<HashablePattern> shared;
|
||||
std::unordered_map<long long, std::vector<ReversiblePattern>> byDialogId;
|
||||
|
||||
for (const auto &filter : filters) {
|
||||
if (!filter.enabled || filter.text.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int flags = UREGEX_MULTILINE;
|
||||
if (filter.caseInsensitive)
|
||||
flags |= UREGEX_CASE_INSENSITIVE;
|
||||
|
||||
|
||||
auto status = U_ZERO_ERROR;
|
||||
auto pattern = RegexPattern::compile(UnicodeString::fromUTF8(filter.text), flags, status);
|
||||
|
||||
if (!pattern) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filter.dialogId.has_value()) {
|
||||
byDialogId[filter.dialogId.value()].push_back({std::shared_ptr<RegexPattern>(pattern), filter.reversed});
|
||||
} else {
|
||||
shared.push_back({filter.id, {std::shared_ptr<RegexPattern>(pattern), filter.reversed}});
|
||||
}
|
||||
}
|
||||
|
||||
auto exclByDialogId = buildExclusions(exclusions, shared);
|
||||
|
||||
sharedPatterns = shared;
|
||||
patternsByDialogId = byDialogId;
|
||||
exclusionsByDialogId = exclByDialogId;
|
||||
filteredMessages.clear();
|
||||
}
|
||||
|
||||
std::unordered_map<long long, std::unordered_set<HashablePattern, PatternHasher>> buildExclusions(
|
||||
const std::vector<RegexFilterGlobalExclusion> &exclusions,
|
||||
const std::vector<HashablePattern> &shared) {
|
||||
|
||||
std::unordered_map<long long, std::unordered_set<HashablePattern, PatternHasher>> exclusionsByDialogId;
|
||||
|
||||
for (const auto &exclusion : exclusions) {
|
||||
|
||||
auto &exclusionSet = exclusionsByDialogId[exclusion.dialogId];
|
||||
|
||||
for (const auto &filter : shared) {
|
||||
if (filter.id == exclusion.filterId) {
|
||||
|
||||
exclusionSet.insert(filter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return exclusionsByDialogId;
|
||||
}
|
||||
|
||||
std::optional<bool> isFiltered(not_null<HistoryItem *> item) {
|
||||
std::lock_guard lock(mutex);
|
||||
auto dialogIt = filteredMessages.find(item->history()->peer->id.value);
|
||||
|
||||
if (dialogIt == filteredMessages.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto it = dialogIt->second.find(item->id.bare);
|
||||
if (it == dialogIt->second.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void putFiltered(not_null<HistoryItem *> item, bool res) {
|
||||
std::lock_guard lock(mutex);
|
||||
filteredMessages[item->history()->peer->id.value][item->id.bare] = res;
|
||||
}
|
||||
|
||||
std::optional<std::vector<ReversiblePattern>> getPatternsByDialogId(uint64 dialogId) {
|
||||
if (!patternsByDialogId.has_value()) {
|
||||
rebuildCache();
|
||||
}
|
||||
const auto it = patternsByDialogId.value().find(dialogId);
|
||||
if (it == patternsByDialogId.value().end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::optional<const std::unordered_set<HashablePattern, PatternHasher>> getExclusionsByDialogId(long long dialogId) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!exclusionsByDialogId.has_value()) {
|
||||
rebuildCache();
|
||||
}
|
||||
const auto it = exclusionsByDialogId.value().find(dialogId);
|
||||
if (it == exclusionsByDialogId.value().end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::vector<HashablePattern> &getSharedPatterns() {
|
||||
if (!sharedPatterns.has_value()) {
|
||||
rebuildCache();
|
||||
}
|
||||
return sharedPatterns.value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
|
||||
#include "filters_controller.h"
|
||||
#include "ayu/data/entities.h"
|
||||
|
||||
using namespace FiltersController;
|
||||
|
||||
namespace FiltersCacheController {
|
||||
|
||||
|
||||
void rebuildCache();
|
||||
|
||||
std::unordered_map<long long, std::unordered_set<HashablePattern, PatternHasher>> buildExclusions(
|
||||
const std::vector<RegexFilterGlobalExclusion>& exclusions,
|
||||
const std::vector<HashablePattern>& shared);
|
||||
|
||||
std::optional<bool> isFiltered(not_null<HistoryItem *> item);
|
||||
void putFiltered(not_null<HistoryItem *> item, bool res);
|
||||
|
||||
std::optional<std::vector<ReversiblePattern>> getPatternsByDialogId(uint64 dialogId);
|
||||
std::optional<const std::unordered_set<HashablePattern, PatternHasher>> getExclusionsByDialogId(long long dialogId);
|
||||
const std::vector<HashablePattern> &getSharedPatterns();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
|
||||
#include "filters_controller.h"
|
||||
|
||||
#include "filters_cache_controller.h"
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "unicode/regex.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "ayu/data/entities.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer_id.h"
|
||||
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_item_components.h"
|
||||
|
||||
#include "filters_utils.h"
|
||||
#include "shadow_ban_utils.h"
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
|
||||
namespace FiltersController {
|
||||
|
||||
bool filterBlocked(const not_null<HistoryItem*> item) {
|
||||
if (item->from() != item->history()->peer) {
|
||||
if (isBlocked(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<bool> isFiltered(const QString &str, uint64 dialogId) {
|
||||
if (str.isEmpty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto icuStr = UnicodeString(reinterpret_cast<const UChar*>(str.constData()), str.length());
|
||||
|
||||
const auto matches = [&](const ReversiblePattern &pattern)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
auto match = pattern.pattern->matcher(icuStr, status)->find();
|
||||
if (U_FAILURE(status)) {
|
||||
LOG(("FILTER FAILED: %1").arg(u_errorName(status)));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto reversed = pattern.reversed;
|
||||
|
||||
if (!reversed && match || reversed && !match) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (const auto &dialogPatterns = FiltersCacheController::getPatternsByDialogId(dialogId); dialogPatterns.has_value() && !dialogPatterns.value().empty()) {
|
||||
for (const auto &pattern : dialogPatterns.value()) {
|
||||
if (matches(pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto &exclusions = FiltersCacheController::getExclusionsByDialogId(dialogId);
|
||||
if (const auto &sharedPatterns = FiltersCacheController::getSharedPatterns(); !sharedPatterns.empty()) {
|
||||
for (const auto &pattern : sharedPatterns) {
|
||||
if (exclusions.has_value() && exclusions.value().contains(pattern)) {
|
||||
continue;
|
||||
}
|
||||
if (matches(pattern.pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEnabled(not_null<PeerData*> peer) {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
return settings.filtersEnabled && (settings.filtersEnabledInChats || !peer->isMegagroup() && !peer->isGigagroup());
|
||||
}
|
||||
|
||||
bool isBlocked(const not_null<HistoryItem*> item) {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
|
||||
const auto blocked = [&]() -> bool
|
||||
{
|
||||
if (item->from()->isUser() &&
|
||||
item->from()->asUser()->isBlocked()) {
|
||||
// don't hide messages if it's a dialog with blocked user
|
||||
return item->from()->asUser()->id != item->history()->peer->id;
|
||||
}
|
||||
|
||||
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
|
||||
if (forwarded->originalSender &&
|
||||
forwarded->originalSender->isUser() &&
|
||||
forwarded->originalSender->asUser()->isBlocked()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
return settings.filtersEnabled &&
|
||||
(
|
||||
item->from()->isUser() && ShadowBanUtils::isShadowBanned(getDialogIdFromPeer(item->from())) ||
|
||||
settings.hideFromBlocked && blocked
|
||||
);
|
||||
}
|
||||
|
||||
bool filtered(const not_null<HistoryItem*> item) {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
if (!settings.filtersEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item->out()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterBlocked(item)) return true;
|
||||
|
||||
if (!isEnabled(item->history()->peer)) return false;
|
||||
|
||||
const auto cached = FiltersCacheController::isFiltered(item);
|
||||
if (cached.has_value()) {
|
||||
return cached.value();
|
||||
}
|
||||
const auto res = isFiltered(FilterUtils::extractAllText(item), getDialogIdFromPeer(item->history()->peer));
|
||||
|
||||
// sometimes item has empty text.
|
||||
// so we cache result only if
|
||||
// processed item is filterable
|
||||
if (res.has_value()) {
|
||||
FiltersCacheController::putFiltered(item, res.value());
|
||||
return res.value();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "unicode/regex.h"
|
||||
|
||||
using namespace icu_78;
|
||||
|
||||
namespace FiltersController {
|
||||
|
||||
bool isEnabled(PeerData *peer);
|
||||
bool isBlocked(not_null<HistoryItem*> item);
|
||||
bool filteredWithoutCaching(not_null<HistoryItem*> historyItem);
|
||||
bool filtered(not_null<HistoryItem*> historyItem);
|
||||
|
||||
struct ReversiblePattern
|
||||
{
|
||||
std::shared_ptr<RegexPattern> pattern;
|
||||
bool reversed;
|
||||
};
|
||||
|
||||
struct HashablePattern
|
||||
{
|
||||
std::vector<char> id;
|
||||
ReversiblePattern pattern;
|
||||
|
||||
bool operator==(const HashablePattern &other) const {
|
||||
return id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct PatternHasher
|
||||
{
|
||||
std::size_t operator()(const HashablePattern &p) const {
|
||||
std::string_view view(p.id.data(), p.id.size());
|
||||
return std::hash<std::string_view>{}(view);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#include "filters_utils.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QClipboard>
|
||||
#include <QGuiApplication>
|
||||
#include <QJsonArray>
|
||||
#include <qjsondocument.h>
|
||||
#include <QString>
|
||||
#include <vector>
|
||||
#include <QtNetwork/QHttpPart>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include "filters_cache_controller.h"
|
||||
#include "lang_auto.h"
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/toast/toast.h"
|
||||
|
||||
constexpr auto BACKUP_VERSION = 2;
|
||||
|
||||
void FilterUtils::importFromLink(const QString &link) {
|
||||
if (link.isEmpty()) {
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailFetch(tr::now));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto request = QNetworkRequest(QUrl(link));
|
||||
_reply = _manager->get(request);
|
||||
|
||||
connect(
|
||||
_reply,
|
||||
&QNetworkReply::finished,
|
||||
this,
|
||||
[=]
|
||||
{
|
||||
const auto responseData = _reply->readAll();
|
||||
|
||||
const auto jsonString = QString::fromUtf8(responseData);
|
||||
|
||||
if (jsonString.isNull()) {
|
||||
LOG(("FilterUtils: Invalid response."));
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailImport(tr::now));
|
||||
|
||||
_reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handleResponse(jsonString.toUtf8())) {
|
||||
LOG(("FilterUtils: Error handling response."));
|
||||
}
|
||||
_reply->deleteLater();
|
||||
});
|
||||
|
||||
connect(
|
||||
_reply,
|
||||
&QNetworkReply::errorOccurred,
|
||||
this,
|
||||
[=](QNetworkReply::NetworkError e)
|
||||
{
|
||||
gotFailure(e);
|
||||
|
||||
_reply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void FilterUtils::publishFilters() {
|
||||
const auto exported = exportFilters();
|
||||
|
||||
auto multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart contentPart;
|
||||
contentPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"content\""));
|
||||
contentPart.setBody(exported.toUtf8());
|
||||
|
||||
QHttpPart syntaxPart;
|
||||
syntaxPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"syntax\""));
|
||||
syntaxPart.setBody("json");
|
||||
|
||||
QHttpPart titlePart;
|
||||
titlePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\""));
|
||||
titlePart.setBody("AyuGram Filters");
|
||||
|
||||
multiPart->append(contentPart);
|
||||
multiPart->append(syntaxPart);
|
||||
multiPart->append(titlePart);
|
||||
|
||||
QNetworkRequest request(QUrl("https://dpaste.com/api/v2/"));
|
||||
|
||||
_reply = _manager->post(request, multiPart);
|
||||
multiPart->setParent(_reply);
|
||||
|
||||
connect(
|
||||
_reply,
|
||||
&QNetworkReply::finished,
|
||||
this,
|
||||
[=]
|
||||
{
|
||||
const auto error = _reply->error();
|
||||
const auto location = _reply->header(QNetworkRequest::LocationHeader);
|
||||
|
||||
if (error == QNetworkReply::NoError && location.isValid()) {
|
||||
auto url = location.toString();
|
||||
url.append(".txt");
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
|
||||
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
|
||||
} else {
|
||||
LOG(("Failed to publish filters to dpaste, error: %1").arg(_reply->errorString()));
|
||||
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailPublish(tr::now));
|
||||
}
|
||||
_reply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool FilterUtils::importFromJson(const QByteArray &json) {
|
||||
auto error = QJsonParseError{0, QJsonParseError::NoError};
|
||||
const auto document = QJsonDocument::fromJson(json, &error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailImport(tr::now));
|
||||
LOG(("FilterUtils: Failed to parse JSON, error: %1"
|
||||
).arg(error.errorString()));
|
||||
return false;
|
||||
}
|
||||
if (!document.isObject()) {
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailImport(tr::now));
|
||||
LOG(("FilterUtils: not an object received in JSON"));
|
||||
return false;
|
||||
}
|
||||
const auto changes = prepareChanges(document.object());
|
||||
|
||||
if (changes == ApplyChanges{}) {
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailImport(tr::now));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto any = !changes.newFilters.empty() || !changes.removeFiltersById.empty() || !changes.filtersOverrides.
|
||||
empty() || !changes.newExclusions.empty() || !changes.removeExclusions.empty() || !changes.peersToBeResolved.
|
||||
empty();
|
||||
|
||||
if (!any) {
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailNoChanges(tr::now));
|
||||
return false;
|
||||
}
|
||||
|
||||
applyChanges(changes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct BackupExclusion
|
||||
{
|
||||
ID dialogId;
|
||||
std::vector<char> filterId;
|
||||
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject json;
|
||||
json["dialogId"] = dialogId;
|
||||
|
||||
// make it look like java's UUID
|
||||
auto hexId = QString(QByteArray(filterId.data(), filterId.size()).toHex());
|
||||
if (hexId.length() == 32) {
|
||||
hexId.insert(8, '-');
|
||||
hexId.insert(13, '-');
|
||||
hexId.insert(18, '-');
|
||||
hexId.insert(23, '-');
|
||||
}
|
||||
json["filterId"] = hexId;
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
QString FilterUtils::exportFilters() {
|
||||
auto createJsonArray = [&](const auto &container)
|
||||
{
|
||||
QJsonArray jsonArray;
|
||||
for (const auto &item : container) {
|
||||
jsonArray.append(item.toJson());
|
||||
}
|
||||
return jsonArray;
|
||||
};
|
||||
|
||||
QJsonArray filtersArray;
|
||||
QJsonObject jsonObject;
|
||||
jsonObject["version"] = BACKUP_VERSION;
|
||||
const auto filters = AyuDatabase::getAllRegexFilters();
|
||||
|
||||
for (const auto &item : filters) {
|
||||
QJsonObject filterJson;
|
||||
filterJson["caseInsensitive"] = item.caseInsensitive;
|
||||
if (item.dialogId.has_value()) {
|
||||
filterJson["dialogId"] = item.dialogId.value();
|
||||
} else {
|
||||
filterJson["dialogId"] = QJsonValue();
|
||||
}
|
||||
filterJson["enabled"] = item.enabled;
|
||||
filterJson["reversed"] = item.reversed;
|
||||
filterJson["text"] = QString::fromStdString(item.text);
|
||||
|
||||
// make it look like java's UUID
|
||||
auto hexId = QString(QByteArray(item.id.data(), item.id.size()).toHex());
|
||||
if (hexId.length() == 32) {
|
||||
hexId.insert(8, '-');
|
||||
hexId.insert(13, '-');
|
||||
hexId.insert(18, '-');
|
||||
hexId.insert(23, '-');
|
||||
}
|
||||
filterJson["id"] = hexId;
|
||||
filtersArray.append(filterJson);
|
||||
}
|
||||
jsonObject["filters"] = filtersArray;
|
||||
|
||||
const auto excl = AyuDatabase::getAllFiltersExclusions();
|
||||
|
||||
|
||||
std::vector<BackupExclusion> exclusions;
|
||||
exclusions.reserve(excl.size());
|
||||
|
||||
for (const auto &item : excl) {
|
||||
exclusions.push_back(BackupExclusion{item.dialogId, item.filterId});
|
||||
}
|
||||
|
||||
jsonObject["exclusions"] = createJsonArray(exclusions);
|
||||
jsonObject["removeFiltersById"] = QJsonValue();
|
||||
jsonObject["removeExclusions"] = QJsonValue();
|
||||
|
||||
QJsonObject peers;
|
||||
for (const auto &item : filters) {
|
||||
const auto &session = currentSession();
|
||||
if (!item.dialogId.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (const auto peer = session->data().peer(peerFromChat(abs(item.dialogId.value())))) {
|
||||
if (!peer->username().isEmpty()) {
|
||||
QString key = QString::number(item.dialogId.value());
|
||||
peers[key] = peer->username();
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonObject["peers"] = peers;
|
||||
|
||||
|
||||
QJsonDocument jsonDoc(jsonObject);
|
||||
QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Indented);
|
||||
return QString::fromUtf8(jsonData);
|
||||
}
|
||||
|
||||
// for compatibility with Android version
|
||||
|
||||
int typeOfMessage(const HistoryItem *item) {
|
||||
if (item->isSponsored()) {
|
||||
return 0; // TYPE_TEXT
|
||||
}
|
||||
|
||||
if (!item->isService()) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto invoice = media->invoice(); invoice && invoice->isPaidMedia) {
|
||||
return 29; // TYPE_PAID_MEDIA
|
||||
}
|
||||
if (media->giveawayStart()) {
|
||||
return 26; // TYPE_GIVEAWAY
|
||||
}
|
||||
if (media->giveawayResults()) {
|
||||
return 28; // TYPE_GIVEAWAY_RESULTS
|
||||
}
|
||||
if (const auto dice = dynamic_cast<Data::MediaDice*>(media)) {
|
||||
return 15; // TYPE_ANIMATED_STICKER
|
||||
}
|
||||
if (media->photo()) {
|
||||
return 1; // TYPE_PHOTO
|
||||
}
|
||||
if (media->location()) {
|
||||
return 4; // TYPE_GEO
|
||||
}
|
||||
if (media->sharedContact()) {
|
||||
return 12; // TYPE_CONTACT
|
||||
}
|
||||
if (media->poll() || media->todolist()) {
|
||||
return 17; // TYPE_POLL
|
||||
}
|
||||
if (media->storyId().valid()) {
|
||||
if (media->storyMention()) {
|
||||
return 24; // TYPE_STORY_MENTION
|
||||
}
|
||||
return 23; // TYPE_STORY
|
||||
}
|
||||
if (const auto document = media->document()) {
|
||||
if (document->round()) {
|
||||
return 5; // TYPE_ROUND_VIDEO
|
||||
}
|
||||
if (document->isVideoFile()) {
|
||||
return 3; // TYPE_VIDEO
|
||||
}
|
||||
if (document->isVoiceMessage()) {
|
||||
return 2; // TYPE_VOICE
|
||||
}
|
||||
if (document->isAudioFile()) {
|
||||
return 14; // TYPE_MUSIC
|
||||
}
|
||||
if (document->isAnimation()) {
|
||||
return 8; // TYPE_GIF
|
||||
}
|
||||
if (document->sticker()) {
|
||||
if (document->isAnimation()) {
|
||||
return 15; // TYPE_ANIMATED_STICKER
|
||||
}
|
||||
return 13; // TYPE_STICKER
|
||||
}
|
||||
return 9; // TYPE_FILE
|
||||
}
|
||||
if (media->game() || media->invoice() || media->webpage()) {
|
||||
return 0; // TYPE_TEXT
|
||||
}
|
||||
} else {
|
||||
if (item->isOnlyEmojiAndSpaces()) {
|
||||
return 19; // TYPE_EMOJIS
|
||||
}
|
||||
return 0; // TYPE_TEXT
|
||||
}
|
||||
} else {
|
||||
if (const auto media = item->media()) {
|
||||
if (media->call()) {
|
||||
return 16; // TYPE_PHONE_CALL
|
||||
}
|
||||
if (media->photo() && !item->isUserpicSuggestion()) {
|
||||
return 11; // TYPE_ACTION_PHOTO
|
||||
}
|
||||
if (media->photo() && item->isUserpicSuggestion()) {
|
||||
return 21; // TYPE_SUGGEST_PHOTO
|
||||
}
|
||||
if (media->paper()) {
|
||||
return 22; // TYPE_ACTION_WALLPAPER
|
||||
}
|
||||
if (const auto gift = media->gift()) {
|
||||
if (gift->type == Data::GiftType::Premium) {
|
||||
if (gift->channel) {
|
||||
return 25; // TYPE_GIFT_PREMIUM_CHANNEL
|
||||
}
|
||||
return 18; // TYPE_GIFT_PREMIUM
|
||||
}
|
||||
if (gift->type == Data::GiftType::Credits
|
||||
|| gift->type == Data::GiftType::StarGift
|
||||
|| gift->type == Data::GiftType::Ton) {
|
||||
return 30; // TYPE_GIFT_STARS
|
||||
}
|
||||
}
|
||||
if (item->Get<HistoryServiceGiveawayResults>()) {
|
||||
return 28; // TYPE_GIVEAWAY_RESULTS
|
||||
}
|
||||
}
|
||||
return 10; // TYPE_DATE
|
||||
}
|
||||
return 0; // TYPE_TEXT
|
||||
}
|
||||
|
||||
QString FilterUtils::extractAllText(not_null<HistoryItem*> item) {
|
||||
QString text(item->originalText().text);
|
||||
if (!item->originalText().entities.empty()) {
|
||||
for (const auto &entity : item->originalText().entities) {
|
||||
if (entity.type() == EntityType::Url || entity.type() == EntityType::CustomUrl) {
|
||||
text.append("\n");
|
||||
text.append(entity.data());
|
||||
}
|
||||
}
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
|
||||
if (!markup->data.isNull()) {
|
||||
for (const auto &row : markup->data.rows) {
|
||||
for (const auto &button : row) {
|
||||
text.append("<button>").append(button.text).append(" ").append(qs(button.data)).append("</button>");
|
||||
text.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text.append("\n").append("<type>").append(QString::number(typeOfMessage(item))).append("</type>");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool FilterUtils::handleResponse(const QByteArray &response) {
|
||||
try {
|
||||
return importFromJson(response);
|
||||
} catch (...) {
|
||||
LOG(("FilterUtils: Failed to apply response"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterUtils::gotFailure(const QNetworkReply::NetworkError &error) {
|
||||
LOG(("FilterUtils: Error %1").arg(error));
|
||||
Ui::Toast::Show(tr::ayu_FiltersToastFailFetch(tr::now));
|
||||
}
|
||||
|
||||
ApplyChanges FilterUtils::prepareChanges(const QJsonObject &root) {
|
||||
const auto version = root.value("version");
|
||||
|
||||
if (version.toInt() > BACKUP_VERSION) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
const auto existingFilters = AyuDatabase::getAllRegexFilters();
|
||||
const auto existingExclusions = AyuDatabase::getAllFiltersExclusions();
|
||||
|
||||
std::vector<RegexFilter> filtersOverrides;
|
||||
std::map<std::vector<char>, RegexFilter> newFilters;
|
||||
std::vector<RegexFilterGlobalExclusion> newExclusions;
|
||||
std::vector<QString> removeFiltersById;
|
||||
std::vector<RegexFilterGlobalExclusion> removeExclusions;
|
||||
std::map<long long, QString> peersToBeResolved;
|
||||
|
||||
|
||||
if (const auto &filters = root.value("filters").toArray(); !filters.isEmpty()) {
|
||||
for (const auto &filterRef : filters) {
|
||||
if (const auto filter = filterRef.toObject(); !filter.isEmpty()) {
|
||||
RegexFilter regex;
|
||||
regex.caseInsensitive = filter.value("caseInsensitive").toBool();
|
||||
|
||||
const auto dialogIdValue = filter.value("dialogId");
|
||||
if (!dialogIdValue.isNull()) {
|
||||
regex.dialogId = filter.value("dialogId").toVariant().toLongLong();
|
||||
} else {
|
||||
regex.dialogId = std::nullopt;
|
||||
}
|
||||
regex.enabled = filter.value("enabled").toBool();
|
||||
|
||||
auto idString = filter.value("id").toString();
|
||||
idString.remove('-');
|
||||
|
||||
auto idBytes = QByteArray::fromHex(idString.toUtf8());
|
||||
regex.id = std::vector(idBytes.constData(), idBytes.constData() + idBytes.size());
|
||||
|
||||
regex.reversed = filter.value("reversed").toBool();
|
||||
regex.text = filter.value("text").toString().toStdString();
|
||||
|
||||
|
||||
auto it = std::ranges::find_if(existingFilters,
|
||||
[®ex](const RegexFilter &f)
|
||||
{
|
||||
return f.id == regex.id;
|
||||
});
|
||||
if (it != existingFilters.end()) {
|
||||
const RegexFilter &existing = *it;
|
||||
if (existing != regex) {
|
||||
filtersOverrides.push_back(existing);
|
||||
}
|
||||
} else {
|
||||
newFilters[regex.id] = std::move(regex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto exclusions = root.value("exclusions").toArray(); !exclusions.isEmpty()) {
|
||||
for (const auto &exclusionRef : exclusions) {
|
||||
if (const auto exclusion = exclusionRef.toObject(); !exclusion.isEmpty()) {
|
||||
RegexFilterGlobalExclusion regex;
|
||||
|
||||
regex.dialogId = exclusion.value("dialogId").toVariant().toLongLong();
|
||||
|
||||
auto filterIdString = exclusion.value("filterId").toString();
|
||||
filterIdString.remove('-');
|
||||
|
||||
auto filterIdBytes = QByteArray::fromHex(filterIdString.toUtf8());
|
||||
regex.filterId = std::vector(
|
||||
filterIdBytes.constData(),
|
||||
filterIdBytes.constData() + filterIdBytes.size()
|
||||
);
|
||||
|
||||
auto it = std::ranges::find_if(
|
||||
existingExclusions,
|
||||
[®ex](const RegexFilterGlobalExclusion &f)
|
||||
{
|
||||
return f.dialogId == regex.dialogId && f.filterId == regex.filterId;
|
||||
});
|
||||
|
||||
if (it == existingExclusions.end()) {
|
||||
newExclusions.push_back(std::move(regex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto removeFiltersByIdJson = root.value("removeFiltersById").toArray(); !removeFiltersByIdJson.
|
||||
isEmpty()) {
|
||||
for (const auto &filterRef : removeFiltersByIdJson) {
|
||||
const auto filter = filterRef.toString();
|
||||
|
||||
const auto byteArray = filter.toUtf8();
|
||||
const auto filterId = std::vector(byteArray.constData(), byteArray.constData() + byteArray.size());
|
||||
|
||||
const auto exists = std::ranges::any_of(
|
||||
existingFilters,
|
||||
[&](const RegexFilter &f)
|
||||
{
|
||||
return f.id == filterId;
|
||||
});
|
||||
if (exists) {
|
||||
removeFiltersById.push_back(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto removeExclusionsJson = root.value("removeExclusions").toArray(); !removeExclusionsJson.isEmpty()) {
|
||||
for (const auto &exclusionRef : removeExclusionsJson) {
|
||||
const auto exclusionObj = exclusionRef.toObject();
|
||||
const auto filterIdStr = exclusionObj.value("filterId").toString();
|
||||
const qint64 dialogId = exclusionObj.value("dialogId").toVariant().toLongLong();
|
||||
|
||||
const auto byteArray = filterIdStr.toUtf8();
|
||||
const auto filterIdVec = std::vector<char>(byteArray.constData(), byteArray.constData() + byteArray.size());
|
||||
|
||||
const bool exists = std::ranges::any_of(
|
||||
existingExclusions,
|
||||
[&](const RegexFilterGlobalExclusion &x)
|
||||
{
|
||||
return x.filterId == filterIdVec && x.dialogId == dialogId;
|
||||
});
|
||||
|
||||
if (exists) {
|
||||
RegexFilterGlobalExclusion regex;
|
||||
regex.dialogId = dialogId;
|
||||
regex.filterId = filterIdVec;
|
||||
removeExclusions.push_back(regex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto peersJson = root.value("peers").toObject(); !peersJson.isEmpty()) {
|
||||
for (const auto &dialogIdStr : peersJson.keys()) {
|
||||
bool parsed;
|
||||
const auto dialogId = dialogIdStr.toLongLong(&parsed);
|
||||
if (!parsed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// almost everytime fails
|
||||
PeerData *peerMaybe = nullptr;
|
||||
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
||||
if (const auto session = account->maybeSession()) {
|
||||
if (const auto peer = session->data().peer(peerFromChat(abs(dialogId)))) {
|
||||
peerMaybe = peer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!peerMaybe) {
|
||||
const auto username = peersJson.value(dialogIdStr).toString();
|
||||
peersToBeResolved[dialogId] = username;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyChanges changes;
|
||||
for (auto &filter : newFilters) {
|
||||
changes.newFilters.push_back(std::move(filter.second));
|
||||
}
|
||||
changes.removeFiltersById = std::move(removeFiltersById);
|
||||
changes.filtersOverrides = std::move(filtersOverrides);
|
||||
changes.newExclusions = std::move(newExclusions);
|
||||
changes.removeExclusions = std::move(removeExclusions);
|
||||
changes.peersToBeResolved = std::move(peersToBeResolved);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
void FilterUtils::applyChanges(const ApplyChanges &changes) {
|
||||
if (!changes.newFilters.empty()) {
|
||||
for (const auto &filter : changes.newFilters) {
|
||||
AyuDatabase::addRegexFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.removeFiltersById.empty()) {
|
||||
for (const auto &id : changes.removeFiltersById) {
|
||||
const auto byteArray = id.toUtf8();
|
||||
const auto filterId = std::vector<char>(byteArray.constData(), byteArray.constData() + byteArray.size());
|
||||
AyuDatabase::deleteExclusionsByFilterId(filterId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.filtersOverrides.empty()) {
|
||||
for (const auto &filter : changes.filtersOverrides) {
|
||||
AyuDatabase::updateRegexFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.newExclusions.empty()) {
|
||||
for (const auto &exclusion : changes.newExclusions) {
|
||||
AyuDatabase::addRegexExclusion(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.removeExclusions.empty()) {
|
||||
for (const auto &exclusion : changes.removeExclusions) {
|
||||
AyuDatabase::deleteExclusion(exclusion.dialogId, exclusion.filterId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.peersToBeResolved.empty()) {
|
||||
resolveAllChats(changes.peersToBeResolved);
|
||||
}
|
||||
|
||||
FiltersCacheController::rebuildCache();
|
||||
crl::on_main([]
|
||||
{
|
||||
AyuSettings::fire_filtersUpdate();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "ayu/data/entities.h"
|
||||
#include "core/application.h"
|
||||
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_item_components.h"
|
||||
|
||||
struct ApplyChanges
|
||||
{
|
||||
std::vector<RegexFilter> newFilters;
|
||||
std::vector<QString> removeFiltersById;
|
||||
|
||||
std::vector<RegexFilter> filtersOverrides;
|
||||
|
||||
std::vector<RegexFilterGlobalExclusion> newExclusions;
|
||||
std::vector<RegexFilterGlobalExclusion> removeExclusions;
|
||||
|
||||
std::map<long long, QString> peersToBeResolved;
|
||||
|
||||
auto operator<=>(const ApplyChanges&) const = default;
|
||||
};
|
||||
|
||||
|
||||
class FilterUtils final: public QObject
|
||||
{
|
||||
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
static FilterUtils &getInstance() {
|
||||
static FilterUtils instance;
|
||||
return instance;
|
||||
}
|
||||
FilterUtils(const FilterUtils &) = delete;
|
||||
FilterUtils &operator=(const FilterUtils &) = delete;
|
||||
FilterUtils(FilterUtils &&) = delete;
|
||||
FilterUtils &operator=(FilterUtils &&) = delete;
|
||||
|
||||
|
||||
void importFromLink(const QString &link);
|
||||
bool importFromJson(const QByteArray &json);
|
||||
|
||||
void publishFilters();
|
||||
static QString exportFilters();
|
||||
|
||||
static QString extractAllText(not_null<HistoryItem *> item);
|
||||
private:
|
||||
FilterUtils():
|
||||
_manager(std::make_unique<QNetworkAccessManager>()) {
|
||||
|
||||
}
|
||||
|
||||
bool handleResponse(const QByteArray &response);
|
||||
void gotFailure(const QNetworkReply::NetworkError &error);
|
||||
|
||||
ApplyChanges prepareChanges(const QJsonObject &response);
|
||||
void applyChanges(const ApplyChanges &changes);
|
||||
|
||||
QTimer* _timer = nullptr;
|
||||
|
||||
std::unique_ptr<QNetworkAccessManager> _manager = nullptr;
|
||||
QNetworkReply *_reply = nullptr;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#include "shadow_ban_utils.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include "filters_cache_controller.h"
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/data/entities.h"
|
||||
|
||||
void ShadowBanUtils::addShadowBan(ID userId) {
|
||||
auto &settings = AyuSettings::getInstance();
|
||||
if (settings.shadowBanIds.insert(userId).second) {
|
||||
setShadowBanList();
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowBanUtils::removeShadowBan(ID userId) {
|
||||
auto &settings = AyuSettings::getInstance();
|
||||
if (settings.shadowBanIds.erase(userId) > 0) {
|
||||
setShadowBanList();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShadowBanUtils::isShadowBanned(ID userId) {
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
return settings.shadowBanIds.contains(userId);
|
||||
}
|
||||
|
||||
void ShadowBanUtils::setShadowBanList() {
|
||||
FiltersCacheController::rebuildCache();
|
||||
AyuSettings::fire_filtersUpdate();
|
||||
AyuSettings::save();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2025
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
class ShadowBanUtils {
|
||||
public:
|
||||
static void addShadowBan(long long userId);
|
||||
static void removeShadowBan(long long userId);
|
||||
static bool isShadowBanned(long long userId);
|
||||
|
||||
private:
|
||||
ShadowBanUtils() = delete;
|
||||
|
||||
static void setShadowBanList();
|
||||
};
|
||||
@@ -38,8 +38,7 @@ bool isForwarding(const PeerId &id) {
|
||||
return state.state != ForwardState::State::Finished
|
||||
&& state.currentChunk < state.totalChunks
|
||||
&& !state.stopRequested
|
||||
&& state.totalChunks
|
||||
&& state.totalMessages;
|
||||
&& (state.totalChunks && state.totalMessages || state.state == ForwardState::State::Downloading);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -85,7 +84,7 @@ std::pair<QString, QString> stateName(const PeerId &id) {
|
||||
if (state->state == ForwardState::State::Preparing) {
|
||||
status = tr::ayu_AyuForwardStatusPreparing(tr::now);
|
||||
} else if (state->state == ForwardState::State::Downloading) {
|
||||
status = tr::ayu_AyuForwardStatusLoadingMedia(tr::now);
|
||||
return std::make_pair(tr::ayu_AyuForwardStatusLoadingMedia(tr::now), "");
|
||||
} else if (state->state == ForwardState::State::Sending) {
|
||||
status = tr::ayu_AyuForwardStatusForwarding(tr::now);
|
||||
} else {
|
||||
@@ -191,7 +190,7 @@ void sendMedia(
|
||||
data,
|
||||
primaryMedia->document()->duration(),
|
||||
mediaType == SendMediaType::Round,
|
||||
message.action);
|
||||
std::move(message));
|
||||
return;
|
||||
}
|
||||
// at least try to send it as squared-video
|
||||
@@ -213,16 +212,15 @@ void sendMedia(
|
||||
}
|
||||
|
||||
bool isAyuForwardNeeded(const std::vector<not_null<HistoryItem*>> &items) {
|
||||
for (const auto &item : items) {
|
||||
if (isAyuForwardNeeded(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
const auto needAyuForward = [&](const auto &item)
|
||||
{
|
||||
return isAyuForwardNeeded(item);
|
||||
};
|
||||
return std::ranges::any_of(items, needAyuForward);
|
||||
}
|
||||
|
||||
bool isAyuForwardNeeded(not_null<HistoryItem*> item) {
|
||||
if (item->isDeleted() || item->isAyuNoForwards() || item->ttlDestroyAt()) {
|
||||
if (item->isDeleted() || item->isAyuNoForwards() || item->unsupportedTTL()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -243,7 +241,10 @@ void intelligentForward(
|
||||
const Api::SendAction &action,
|
||||
const Data::ResolvedForwardDraft &draft) {
|
||||
const auto history = action.history;
|
||||
history->setForwardDraft(action.replyTo.topicRootId, action.replyTo.monoforumPeerId, {});
|
||||
crl::on_main([&]
|
||||
{
|
||||
history->setForwardDraft(action.replyTo.topicRootId, action.replyTo.monoforumPeerId, {});
|
||||
});
|
||||
|
||||
const auto items = draft.items;
|
||||
const auto peer = history->peer;
|
||||
@@ -302,7 +303,7 @@ void forwardMessages(
|
||||
not_null<Main::Session*> session,
|
||||
const Api::SendAction &action,
|
||||
bool forwardState,
|
||||
Data::ResolvedForwardDraft draft) {
|
||||
const Data::ResolvedForwardDraft &draft) {
|
||||
const auto items = draft.items;
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
@@ -365,6 +366,7 @@ void forwardMessages(
|
||||
}
|
||||
|
||||
auto message = Api::MessageToSend(Api::SendAction(session->data().history(peer->id)));
|
||||
message.action.options.invertCaption = item->invertMedia();
|
||||
message.action.replyTo = action.replyTo;
|
||||
|
||||
if (draft.options != Data::ForwardOptions::NoNamesAndCaptions) {
|
||||
@@ -392,6 +394,23 @@ void forwardMessages(
|
||||
}
|
||||
}
|
||||
|
||||
// remove not finished files
|
||||
for (int j = preparedMedia.files.size() - 1; j >= 0; j--) {
|
||||
auto &file = preparedMedia.files[j];
|
||||
|
||||
QFile f(file.path);
|
||||
if (groupMedia[j]->photo() && f.size() < groupMedia[j]->photo()->imageByteSize(Data::PhotoSize::Large)
|
||||
||
|
||||
groupMedia[j]->document() && f.size() < groupMedia[j]->document()->size
|
||||
) {
|
||||
preparedMedia.files.erase(preparedMedia.files.begin() + j);
|
||||
}
|
||||
}
|
||||
|
||||
if (preparedMedia.files.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto groups = Ui::DivideByGroups(
|
||||
std::move(preparedMedia),
|
||||
way,
|
||||
|
||||
@@ -47,6 +47,6 @@ void forwardMessages(
|
||||
not_null<Main::Session*> session,
|
||||
const Api::SendAction &action,
|
||||
bool forwardState,
|
||||
Data::ResolvedForwardDraft draft);
|
||||
const Data::ResolvedForwardDraft &draft);
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
namespace AyuSync {
|
||||
|
||||
QString pathForSave(not_null<Main::Session*> session) {
|
||||
const auto path = Core::App().settings().downloadPath();
|
||||
auto path = Core::App().settings().downloadPath();
|
||||
if (path.isEmpty()) {
|
||||
return File::DefaultDownloadPath(session);
|
||||
}
|
||||
@@ -66,6 +66,10 @@ QString pathForSave(not_null<Main::Session*> session) {
|
||||
}
|
||||
|
||||
QString filePath(not_null<Main::Session*> session, const Data::Media *media) {
|
||||
if (!media) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto document = media->document()) {
|
||||
if (!document->filename().isEmpty()) {
|
||||
return pathForSave(session) + media->document()->filename();
|
||||
@@ -81,11 +85,22 @@ QString filePath(not_null<Main::Session*> session, const Data::Media *media) {
|
||||
return pathForSave(session) + "round_" + QString::number(document->getDC()) + "_" +
|
||||
QString::number(document->id) + ".mp4";
|
||||
}
|
||||
|
||||
|
||||
// media without any file name
|
||||
if (document->isGifv()) {
|
||||
return pathForSave(session) + "gif_" + QString::number(document->getDC()) + "_" +
|
||||
QString::number(document->id) + ".gif";
|
||||
}
|
||||
if (document->isVideoFile()) {
|
||||
return pathForSave(session) + "video_" + QString::number(document->getDC()) + "_" +
|
||||
QString::number(document->id) + ".mp4";
|
||||
}
|
||||
} else if (const auto photo = media->photo()) {
|
||||
return pathForSave(session) + QString::number(photo->getDC()) + "_" + QString::number(photo->id) + ".jpg";
|
||||
}
|
||||
|
||||
return QString();
|
||||
return {};
|
||||
}
|
||||
|
||||
qint64 fileSize(not_null<HistoryItem*> item) {
|
||||
@@ -128,24 +143,41 @@ void loadDocumentSync(not_null<Main::Session*> session, DocumentData *data, not_
|
||||
auto latch = std::make_shared<TimedCountDownLatch>(1);
|
||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
|
||||
auto path = filePath(session, item->media());
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
crl::on_main([&]
|
||||
{
|
||||
data->save(Data::FileOriginMessage(item->fullId()), filePath(session, item->media()));
|
||||
data->save(Data::FileOriginMessage(item->fullId()), path);
|
||||
|
||||
rpl::single() | rpl::then(
|
||||
session->downloaderTaskFinished()
|
||||
) | rpl::filter([&]
|
||||
|
||||
session->downloaderTaskFinished() | rpl::filter([&]
|
||||
{
|
||||
return data->status == FileDownloadFailed || fileSize(item) == data->size;
|
||||
}) | rpl::start_with_next([&]() mutable
|
||||
{
|
||||
latch->countDown();
|
||||
base::take(lifetime)->destroy();
|
||||
},
|
||||
*lifetime);
|
||||
});
|
||||
|
||||
latch->await(std::chrono::minutes(5));
|
||||
|
||||
constexpr auto overall = std::chrono::minutes(15);
|
||||
const auto startTime = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
while (std::chrono::steady_clock::now() - startTime < overall) {
|
||||
if (latch->await(std::chrono::minutes(5))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!data->loading()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
base::take(lifetime)->destroy();
|
||||
}
|
||||
|
||||
void forwardMessagesSync(not_null<Main::Session*> session,
|
||||
@@ -220,11 +252,11 @@ void loadPhotoSync(not_null<Main::Session*> session, const std::pair<not_null<Ph
|
||||
{
|
||||
saveToFiles();
|
||||
latch->countDown();
|
||||
base::take(lifetime)->destroy();
|
||||
},
|
||||
*lifetime);
|
||||
});
|
||||
latch->await(std::chrono::minutes(5));
|
||||
base::take(lifetime)->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,12 +287,12 @@ void waitForMsgSync(not_null<Main::Session*> session, const Api::SendAction &act
|
||||
}) | rpl::start_with_next([&]
|
||||
{
|
||||
latch->countDown();
|
||||
base::take(lifetime)->destroy();
|
||||
},
|
||||
*lifetime);
|
||||
});
|
||||
|
||||
latch->await(std::chrono::minutes(2));
|
||||
latch->await(std::chrono::minutes(5));
|
||||
base::take(lifetime)->destroy();
|
||||
}
|
||||
|
||||
void sendDocumentSync(not_null<Main::Session*> session,
|
||||
@@ -268,13 +300,13 @@ void sendDocumentSync(not_null<Main::Session*> session,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
const Api::SendAction &action) {
|
||||
|
||||
auto groupId = std::make_shared<SendingAlbum>();
|
||||
groupId->groupId = base::RandomValue<uint64>();
|
||||
|
||||
crl::on_main([=, lst = std::move(group.list), caption = std::move(caption)]() mutable
|
||||
{
|
||||
session->api().sendFiles(std::move(lst), type, std::move(caption), groupId, action);
|
||||
auto size = lst.files.size();
|
||||
session->api().sendFiles(std::move(lst), type, std::move(caption), size > 1 ? groupId : nullptr, action);
|
||||
});
|
||||
|
||||
waitForMsgSync(session, action);
|
||||
@@ -296,14 +328,24 @@ void sendVoiceSync(not_null<Main::Session*> session,
|
||||
const QByteArray &data,
|
||||
int64_t duration,
|
||||
bool video,
|
||||
const Api::SendAction &action) {
|
||||
crl::on_main([&]
|
||||
Api::MessageToSend &&message) {
|
||||
const auto action = message.action;
|
||||
|
||||
crl::on_main([=]
|
||||
{
|
||||
session->api().sendVoiceMessage(data,
|
||||
QVector<signed char>(),
|
||||
duration,
|
||||
video,
|
||||
action);
|
||||
const auto to = FileLoadTo(
|
||||
action.history->peer->id,
|
||||
action.options,
|
||||
action.replyTo,
|
||||
action.replaceMediaOf);
|
||||
session->api().fileLoader()->addTask(std::make_unique<FileLoadTask>(
|
||||
session,
|
||||
data,
|
||||
duration,
|
||||
QVector<signed char>(),
|
||||
video,
|
||||
to,
|
||||
message.textWithTags));
|
||||
});
|
||||
waitForMsgSync(session, action);
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@ void sendVoiceSync(not_null<Main::Session*> session,
|
||||
const QByteArray &data,
|
||||
int64_t duration,
|
||||
bool video,
|
||||
const Api::SendAction &action);
|
||||
Api::MessageToSend &&message);
|
||||
}
|
||||
|
||||