mirror of
https://github.com/opengram-server/opengram.git
synced 2026-07-19 12:10:17 +03:00
Initial commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# Docker ignore file - exclude unnecessary files from build context
|
||||
|
||||
# Node.js
|
||||
**/node_modules/
|
||||
**/npm-debug.log
|
||||
**/.npm
|
||||
**/yarn-error.log
|
||||
**/yarn.lock
|
||||
**/package-lock.json
|
||||
|
||||
# .NET build artifacts
|
||||
**/bin/
|
||||
**/obj/
|
||||
**/.vs/
|
||||
**/.vscode/
|
||||
|
||||
# Git
|
||||
**/.git/
|
||||
**/.gitignore
|
||||
**/.gitattributes
|
||||
|
||||
# Docker
|
||||
**/docker-compose*.yml
|
||||
**/Dockerfile*
|
||||
**/.dockerignore
|
||||
|
||||
# Documentation
|
||||
**/*.md
|
||||
**/docs/
|
||||
|
||||
# IDE
|
||||
**/.idea/
|
||||
**/.vscode/
|
||||
**/*.suo
|
||||
**/*.user
|
||||
**/*.userosscache
|
||||
**/*.sln.docstates
|
||||
|
||||
# Logs
|
||||
**/logs/
|
||||
**/*.log
|
||||
|
||||
# Temporary files
|
||||
**/tmp/
|
||||
**/temp/
|
||||
**/.tmp/
|
||||
|
||||
# Specific exclusions
|
||||
stargift-admin/
|
||||
docker/compose/data/
|
||||
scripts/
|
||||
|
||||
# Keep source code and build files for Docker
|
||||
!source/
|
||||
!build/
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
out/
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
.cr/
|
||||
.idea/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# --- Project-specific secrets and local artifacts ---
|
||||
# Приватные ключи и сертификаты
|
||||
*.pem
|
||||
*.key
|
||||
*.pfx
|
||||
# Файлы сессий Telethon/Pyrogram
|
||||
*.session
|
||||
# Резервные копии паролей и временные секреты
|
||||
*.passwords.*
|
||||
.passwords.tmp
|
||||
.env.secure
|
||||
# Логи Node-сервисов
|
||||
*.log
|
||||
|
||||
# Загружаемые в рантайме медиа админ-панели
|
||||
stargift-admin/backend/uploads/*
|
||||
!stargift-admin/backend/uploads/.gitkeep
|
||||
@@ -0,0 +1,92 @@
|
||||
# Opengram
|
||||
|
||||
Opengram — это самостоятельный сервер Telegram, написанный на C# (.NET 9). Проект является форком [mytelegram](https://github.com/loyldg/mytelegram) и реализует серверную часть API Telegram (MTProto), которую можно развернуть на собственной инфраструктуре.
|
||||
|
||||
## Возможности
|
||||
|
||||
- MTProto-транспорты (Abridged, Intermediate);
|
||||
- личные чаты, группы, супергруппы и каналы;
|
||||
- секретные (end-to-end) чаты;
|
||||
- голосовые и видеозвонки (через TURN/STUN и SFU mediasoup);
|
||||
- боты и Bot API;
|
||||
- настройки приватности и двухфакторная аутентификация;
|
||||
- стикеры, реакции, кастомные эмодзи;
|
||||
- звёзды (Stars) и подарки (Star Gifts), включая перепродажу и апгрейд;
|
||||
- истории (Stories), темы оформления и обои;
|
||||
- запланированные и самоудаляющиеся сообщения.
|
||||
|
||||
## Архитектура
|
||||
|
||||
Сервер состоит из набора микросервисов, которые запускаются через Docker Compose:
|
||||
|
||||
| Сервис | Назначение |
|
||||
| --- | --- |
|
||||
| `gateway-server` | Точка входа для MTProto-подключений клиентов |
|
||||
| `auth-server` | Авторизация и обмен ключами |
|
||||
| `session-server` | Хранение сессий и маршрутизация обновлений |
|
||||
| `messenger-command-server` | Обработка команд (запись, CQRS) |
|
||||
| `messenger-query-server` | Обработка запросов (чтение, CQRS) |
|
||||
| `bot-api-server` | HTTP Bot API |
|
||||
| `admin-api` | Служебный API администрирования |
|
||||
| `file-server` / `file-merge-proxy` | Хранение и раздача файлов |
|
||||
| `turn-server` | TURN/STUN для звонков |
|
||||
| `sms-sender` | Отправка кодов подтверждения |
|
||||
| `data-seeder` | Первичное наполнение базы данных |
|
||||
|
||||
Инфраструктура: MongoDB (хранилище и event store), Redis (кеш), RabbitMQ (шина событий), MinIO (объектное хранилище файлов).
|
||||
|
||||
Дополнительные компоненты репозитория:
|
||||
|
||||
- `mediasoup-server/` — SFU для групповых видеозвонков (Node.js);
|
||||
- `stargift-admin/` — веб-панель управления подарками (backend на Node.js, frontend на React);
|
||||
- `scripts/` — вспомогательные скрипты запуска и тестовые боты.
|
||||
|
||||
## Быстрый старт (Docker)
|
||||
|
||||
Потребуются Docker и Docker Compose.
|
||||
|
||||
1. Перейдите в каталог с compose-файлом:
|
||||
|
||||
```bash
|
||||
cd docker/compose
|
||||
```
|
||||
|
||||
2. Откройте файл `.env` и задайте свои значения вместо плейсхолдеров `CHANGE_ME`
|
||||
(пароли MongoDB, Redis, RabbitMQ, MinIO, ключ Admin API), а также укажите внешний
|
||||
IP-адрес сервера в параметрах `App__WebRtcConnections` и `App__DcOptions`.
|
||||
|
||||
3. Сгенерируйте RSA-ключи MTProto и положите их в `docker/compose/secrets/mtproto/`
|
||||
(см. `secrets/mtproto/README.md`).
|
||||
|
||||
4. Запустите сервисы:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
После старта подключите клиент Telegram, прописав адрес вашего дата-центра.
|
||||
|
||||
## Сборка из исходников
|
||||
|
||||
Для сборки нужен .NET 9 SDK.
|
||||
|
||||
```bash
|
||||
cd source
|
||||
dotnet build MyTelegram.sln -c Release
|
||||
```
|
||||
|
||||
Скрипты сборки Docker-образов лежат в каталоге `build/`.
|
||||
|
||||
## Конфигурация
|
||||
|
||||
Все настройки задаются через переменные окружения (файл `.env`) либо через
|
||||
`appsettings.json` отдельных сервисов. В репозитории все значения паролей и ключей
|
||||
заменены на плейсхолдеры `CHANGE_ME` — перед запуском замените их на свои.
|
||||
|
||||
Не храните реальные пароли и приватные ключи в репозитории.
|
||||
|
||||
## Лицензия и происхождение
|
||||
|
||||
Проект основан на [mytelegram](https://github.com/loyldg/mytelegram). Все права на
|
||||
оригинальный код принадлежат его авторам; уважайте условия лицензии исходного проекта
|
||||
и товарные знаки Telegram.
|
||||
@@ -0,0 +1,45 @@
|
||||
$version=Get-Content "./version.txt"
|
||||
$date = Get-Date -Format "Mdd"
|
||||
$version = $version+$date
|
||||
|
||||
$currentDir=(Get-Item .).FullName
|
||||
$parentFolder=(Get-Item $currentDir).Parent
|
||||
$outputRootFolder=Join-Path $parentFolder "out/local" $version
|
||||
$sourceRootFolder=Join-Path $parentFolder "./source/src"
|
||||
|
||||
$authServerOutputFolder=Join-Path $outputRootFolder "auth"
|
||||
$gatewayOutputFolder=Join-Path $outputRootFolder "gateway"
|
||||
$messengerProCommandOutputFolder=Join-Path $outputRootFolder "command-server"
|
||||
$messengerProQueryOutputFolder=Join-Path $outputRootFolder "query-server"
|
||||
$messengerGrpcOutputFolder=Join-Path $outputRootFolder "messenger-grpc"
|
||||
$smsOutputFolder=Join-Path $outputRootFolder "sms"
|
||||
$dataSeederOutputFolder=Join-Path $outputRootFolder "data-seeder"
|
||||
|
||||
Write-Output "Current dir:$currentDir"
|
||||
Write-Output "OutputFolder is:$outputRootFolder"
|
||||
|
||||
function CreateFolderIfNotExists([System.String] $folder){
|
||||
if(![System.IO.Directory]::Exists($folder)){
|
||||
Write-Output "Create new folder:$folder"
|
||||
[System.IO.Directory]::CreateDirectory($folder)
|
||||
}
|
||||
}
|
||||
|
||||
function Build-Server([System.String]$srcFolder,[System.String] $outputFolder) {
|
||||
$sourceFolder=Join-Path $sourceRootFolder $srcFolder
|
||||
dotnet publish $sourceFolder -c Release -o $outputFolder
|
||||
}
|
||||
|
||||
# Set-Location ../source/
|
||||
#dotnet restore ./MyTelegram.sln
|
||||
Build-Server "./MyTelegram.AuthServer" $authServerOutputFolder
|
||||
Build-Server "./MyTelegram.GatewayServer" $gatewayOutputFolder
|
||||
Build-Server "./MyTelegram.Messenger.CommandServer" $messengerProCommandOutputFolder
|
||||
Build-Server "./MyTelegram.Messenger.QueryServer" $messengerProQueryOutputFolder
|
||||
#Build-Server "./MyTelegram.MessengerServer.GrpcService" $messengerGrpcOutputFolder
|
||||
Build-Server "./MyTelegram.SmsSender" $smsOutputFolder
|
||||
Build-Server "./MyTelegram.DataSeeder" $dataSeederOutputFolder
|
||||
|
||||
Set-Location $currentDir
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
v=$(head -n 1 ./version.txt)
|
||||
currentDate=`date +%-m%d`
|
||||
version=$v.$currentDate
|
||||
|
||||
currentDir=$(pwd)
|
||||
parentFolder=$(dirname "$currentDir")
|
||||
outputRootFolder="$parentFolder/out/local/$version"
|
||||
sourceRootFolder="$parentFolder/source/src"
|
||||
|
||||
authServerOutputFolder="$outputRootFolder/auth"
|
||||
gatewayOutputFolder="$outputRootFolder/gateway"
|
||||
messengerProCommandOutputFolder="$outputRootFolder/command-server"
|
||||
messengerProQueryOutputFolder="$outputRootFolder/query-server"
|
||||
messengerGrpcOutputFolder="$outputRootFolder/messenger-grpc"
|
||||
smsOutputFolder="$outputRootFolder/sms"
|
||||
dataSeederOutputFolder="$outputRootFolder/data-seeder"
|
||||
|
||||
echo "Current dir: $currentDir"
|
||||
echo "OutputFolder is: $outputRootFolder"
|
||||
|
||||
CreateFolderIfNotExists() {
|
||||
folder="$1"
|
||||
if [ ! -d "$folder" ]; then
|
||||
echo "Create new folder: $folder"
|
||||
mkdir -p "$folder"
|
||||
fi
|
||||
}
|
||||
|
||||
Build-Server() {
|
||||
srcFolder="$1"
|
||||
outputFolder="$2"
|
||||
sourceFolder="$sourceRootFolder/$srcFolder"
|
||||
dotnet publish "$sourceFolder" -c Release -o "$outputFolder"
|
||||
}
|
||||
|
||||
Build-Server "./MyTelegram.AuthServer" "$authServerOutputFolder"
|
||||
Build-Server "./MyTelegram.GatewayServer" "$gatewayOutputFolder"
|
||||
Build-Server "./MyTelegram.Messenger.CommandServer" "$messengerProCommandOutputFolder"
|
||||
Build-Server "./MyTelegram.Messenger.QueryServer" "$messengerProQueryOutputFolder"
|
||||
#Build-Server "./MyTelegram.MessengerServer.GrpcService" "$messengerGrpcOutputFolder"
|
||||
Build-Server "./MyTelegram.SmsSender" "$smsOutputFolder"
|
||||
Build-Server "./MyTelegram.DataSeeder" "$dataSeederOutputFolder"
|
||||
|
||||
cd "$currentDir"
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-messenger-command-server"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-messenger-query-server"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-sms-sender"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-gateway-server"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-auth-server"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
export MYTELEGRAM_SERVER_NAME="mytelegram-data-seeder"
|
||||
|
||||
. ./helper.sh
|
||||
$BUILD_ALL_COMMAND
|
||||
@@ -0,0 +1,46 @@
|
||||
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/runtime:9.0 AS base
|
||||
USER $APP_UID
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
# This stage is used to build the service project
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
ARG TARGETARCH
|
||||
WORKDIR /src
|
||||
COPY ["Directory.Packages.props", "."]
|
||||
COPY ["Directory.Build.props", "."]
|
||||
COPY ["nuget.config", "."]
|
||||
COPY ["src/MyTelegram.DataSeeder/MyTelegram.DataSeeder.csproj", "src/MyTelegram.DataSeeder/"]
|
||||
COPY ["src/MyTelegram.Domain/MyTelegram.Domain.csproj", "src/MyTelegram.Domain/"]
|
||||
COPY ["src/MyTelegram.Domain.Shared/MyTelegram.Domain.Shared.csproj", "src/MyTelegram.Domain.Shared/"]
|
||||
COPY ["src/MyTelegram.Abstractions/MyTelegram.Abstractions.csproj", "src/MyTelegram.Abstractions/"]
|
||||
COPY ["src/MyTelegram.Schema/MyTelegram.Schema.csproj", "src/MyTelegram.Schema/"]
|
||||
COPY ["src/MyTelegram.EventFlow/MyTelegram.EventFlow.csproj", "src/MyTelegram.EventFlow/"]
|
||||
COPY ["src/MyTelegram.QueryHandlers.MongoDB/MyTelegram.QueryHandlers.MongoDB.csproj", "src/MyTelegram.QueryHandlers.MongoDB/"]
|
||||
COPY ["src/MyTelegram.Queries/MyTelegram.Queries.csproj", "src/MyTelegram.Queries/"]
|
||||
COPY ["src/MyTelegram.ReadModel/MyTelegram.ReadModel.csproj", "src/MyTelegram.ReadModel/"]
|
||||
COPY ["src/MyTelegram.ReadModel.Interfaces/MyTelegram.ReadModel.Interfaces.csproj", "src/MyTelegram.ReadModel.Interfaces/"]
|
||||
COPY ["src/MyTelegram.ReadModel.MongoDB/MyTelegram.ReadModel.MongoDB.csproj", "src/MyTelegram.ReadModel.MongoDB/"]
|
||||
COPY ["src/MyTelegram.EventFlow.MongoDB/MyTelegram.EventFlow.MongoDB.csproj", "src/MyTelegram.EventFlow.MongoDB/"]
|
||||
COPY ["src/MyTelegram.Services/MyTelegram.Services.csproj", "src/MyTelegram.Services/"]
|
||||
COPY ["src/MyTelegram.Core/MyTelegram.Core.csproj", "src/MyTelegram.Core/"]
|
||||
COPY ["src/MyTelegram.EventBus/MyTelegram.EventBus.csproj", "src/MyTelegram.EventBus/"]
|
||||
RUN dotnet restore -a $TARGETARCH "./src/MyTelegram.DataSeeder/MyTelegram.DataSeeder.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/src/MyTelegram.DataSeeder"
|
||||
RUN dotnet build "./MyTelegram.DataSeeder.csproj" -a $TARGETARCH -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
# This stage is used to publish the service project to be copied to the final stage
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./MyTelegram.DataSeeder.csproj" -a $TARGETARCH -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "MyTelegram.DataSeeder.dll"]
|
||||
@@ -0,0 +1,40 @@
|
||||
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/runtime:9.0 AS base
|
||||
USER $APP_UID
|
||||
WORKDIR /app
|
||||
|
||||
# This stage is used to build the service project
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
ARG TARGETARCH
|
||||
WORKDIR /src
|
||||
COPY ["Directory.Packages.props", "."]
|
||||
COPY ["Directory.Build.props", "."]
|
||||
COPY ["nuget.config", "."]
|
||||
COPY ["src/MyTelegram.SmsSender/MyTelegram.SmsSender.csproj", "src/MyTelegram.SmsSender/"]
|
||||
COPY ["src/MyTelegram.Core/MyTelegram.Core.csproj", "src/MyTelegram.Core/"]
|
||||
COPY ["src/MyTelegram.Abstractions/MyTelegram.Abstractions.csproj", "src/MyTelegram.Abstractions/"]
|
||||
COPY ["src/MyTelegram.Domain.Shared/MyTelegram.Domain.Shared.csproj", "src/MyTelegram.Domain.Shared/"]
|
||||
COPY ["src/MyTelegram.Schema/MyTelegram.Schema.csproj", "src/MyTelegram.Schema/"]
|
||||
COPY ["src/MyTelegram.EventBus.RabbitMQ/MyTelegram.EventBus.RabbitMQ.csproj", "src/MyTelegram.EventBus.RabbitMQ/"]
|
||||
COPY ["src/MyTelegram.EventBus/MyTelegram.EventBus.csproj", "src/MyTelegram.EventBus/"]
|
||||
COPY ["src/MyTelegram.Services/MyTelegram.Services.csproj", "src/MyTelegram.Services/"]
|
||||
COPY ["src/MyTelegram.EventFlow/MyTelegram.EventFlow.csproj", "src/MyTelegram.EventFlow/"]
|
||||
RUN dotnet restore -a $TARGETARCH "./src/MyTelegram.SmsSender/MyTelegram.SmsSender.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/src/MyTelegram.SmsSender"
|
||||
RUN dotnet build "./MyTelegram.SmsSender.csproj" -a $TARGETARCH -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
# This stage is used to publish the service project to be copied to the final stage
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
ARG TARGETARCH
|
||||
RUN dotnet publish "./MyTelegram.SmsSender.csproj" -a $TARGETARCH -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "MyTelegram.SmsSender.dll"]
|
||||
@@ -0,0 +1,13 @@
|
||||
### Run the following scripts to build docker images
|
||||
### linux/amd64 (build)
|
||||
```
|
||||
build-all-amd64.sh
|
||||
```
|
||||
### linux/arm64 (build)
|
||||
```
|
||||
build-all-arm64.sh
|
||||
```
|
||||
### linux/amd64 & linux/arm64 (build and push)
|
||||
```
|
||||
build-and-push-all-amd64-arm64.sh
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
./1.build-messenger-command-server.sh
|
||||
./2.build-messenger-query-server.sh
|
||||
./4.build-sms-sender.sh
|
||||
./5.build-gateway-server.sh
|
||||
./6.build-auth-server.sh
|
||||
./7.build-data-seeder.sh
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
export BUILDPLATFORM="linux/arm64"
|
||||
export TARGETARCH="linux/arm64"
|
||||
./1.build-messenger-command-server.sh
|
||||
./2.build-messenger-query-server.sh
|
||||
./4.build-sms-sender.sh
|
||||
./5.build-gateway-server.sh
|
||||
./6.build-auth-server.sh
|
||||
./7.build-data-seeder.sh
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
export BUILDPLATFORM="linux/amd64,linux/arm64"
|
||||
export TARGETARCH="linux/amd64,linux/arm64"
|
||||
#export REGISTRY_URL="localhost:8500"
|
||||
export BUILD_COMMAND="docker buildx build --push"
|
||||
./1.build-messenger-command-server.sh
|
||||
./2.build-messenger-query-server.sh
|
||||
./4.build-sms-sender.sh
|
||||
./5.build-gateway-server.sh
|
||||
./6.build-auth-server.sh
|
||||
./7.build-data-seeder.sh
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
export BUILDPLATFORM="linux/amd64"
|
||||
export TARGETARCH="linux/amd64"
|
||||
export REGISTRY_URL="localhost:8500"
|
||||
export BUILD_COMMAND="docker buildx build --push"
|
||||
./1.build-messenger-command-server.sh
|
||||
./2.build-messenger-query-server.sh
|
||||
./4.build-sms-sender.sh
|
||||
./5.build-gateway-server.sh
|
||||
./6.build-auth-server.sh
|
||||
./7.build-data-seeder.sh
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
v=$(head -n 1 ../version.txt)
|
||||
currentDate=`date +%-m%d`
|
||||
export DEFAULT_VERSION=$v.$currentDate
|
||||
export DEFAULT_IMAGE_VERSION=$DEFAULT_VERSION
|
||||
DEFAULT_PLATFORM="linux/amd64"
|
||||
|
||||
: ${VERSION:=$DEFAULT_VERSION}
|
||||
: ${IMAGE_VERSION:=$DEFAULT_IMAGE_VERSION}
|
||||
: ${TARGETARCH:=$DEFAULT_PLATFORM}
|
||||
: ${BUILDPLATFORM:=$DEFAULT_PLATFORM}
|
||||
: ${BUILD_CONFIGURATION:="Release"}
|
||||
: ${REGISTRY_URL:="mytelegram"}
|
||||
: ${BUILD_COMMAND:="docker build" }
|
||||
: ${MYTELEGRAM_SERVER_NAME:="mytelegram-messenger-command-server"}
|
||||
: ${IMAGE_NAME:=$REGISTRY_URL/$MYTELEGRAM_SERVER_NAME}
|
||||
: ${DOCKER_FILENAME:="./Dockerfile-$MYTELEGRAM_SERVER_NAME"}
|
||||
|
||||
echo Version: $VERSION
|
||||
echo Docker image version: $IMAGE_VERSION
|
||||
echo Target architecture: $TARGETARCH
|
||||
echo Buld configuration: $BUILD_CONFIGURATION
|
||||
|
||||
: ${BUILD_ALL_COMMAND:=$BUILD_COMMAND \
|
||||
--platform $BUILDPLATFORM \
|
||||
--build-arg BUILD_CONFIGURATION=$BUILD_CONFIGURATION \
|
||||
--tag $IMAGE_NAME:$IMAGE_VERSION \
|
||||
--tag $IMAGE_NAME:latest \
|
||||
--file $DOCKER_FILENAME \
|
||||
../../source \
|
||||
--network=host}
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
export REGISTRY_URL="mytelegram"
|
||||
|
||||
./1.build-messenger-command-server.sh
|
||||
./2.build-messenger-query-server.sh
|
||||
./4.build-sms-sender.sh
|
||||
./5.build-gateway-server.sh
|
||||
./6.build-auth-server.sh
|
||||
@@ -0,0 +1 @@
|
||||
0.32.206
|
||||
@@ -0,0 +1,247 @@
|
||||
# MyTelegram servers version
|
||||
MyTelegramVersion=0.32.206.802
|
||||
MyTelegramRegistry=mytelegram
|
||||
|
||||
# Build configuration for compiling from source
|
||||
BUILD_CONFIGURATION=Release
|
||||
# RabbitMQ
|
||||
RabbitMQ__Connections__Default__HostName=rabbitmq
|
||||
RabbitMQ__Connections__Default__Port=5672
|
||||
RabbitMQ__Connections__Default__UserName=test
|
||||
RabbitMQ__Connections__Default__Password=test
|
||||
RabbitMQ__EventBus__ExchangeName=mytelegram_exchange
|
||||
|
||||
# Redis
|
||||
Redis__Configuration=redis:6379,password=CHANGE_ME
|
||||
|
||||
# MongoDB
|
||||
ConnectionStrings__Default=mongodb://mongodb:27017
|
||||
|
||||
# Minio
|
||||
Minio__Endpoint=minio:9000
|
||||
Minio__AccessKey=minioadmin
|
||||
Minio__SecretKey=CHANGE_ME
|
||||
Minio__BucketName=tg-files
|
||||
# Minio__CreateBucketIfNotExists=True
|
||||
|
||||
# TwilioSms
|
||||
# TwilioSms__Enabled=False
|
||||
# TwilioSms__AccountSId=
|
||||
# TwilioSms__AuthToken=
|
||||
# TwilioSms__FromNumber=
|
||||
# TwilioSms__MessagingServiceSId=
|
||||
|
||||
# VonageSms
|
||||
# VonageSms__Enabled=False
|
||||
# VonageSms__BrandName=
|
||||
# VonageSms__ApiKey=
|
||||
# VonageSms__ApiSecret=
|
||||
|
||||
# EmailSenderOptions
|
||||
# EmailSenderOptions__FromAddress=
|
||||
# EmailSenderOptions__FromDisplayName=MyTelegram
|
||||
# EmailSenderOptions__SmtpEmailOptions__Host=
|
||||
# EmailSenderOptions__SmtpEmailOptions__Port=
|
||||
# EmailSenderOptions__SmtpEmailOptions__UserName=
|
||||
# EmailSenderOptions__SmtpEmailOptions__Password=
|
||||
# EmailSenderOptions__SmtpEmailOptions__Domain=localhost
|
||||
# EmailSenderOptions__SmtpEmailOptions__EnableSsl=False
|
||||
# EmailSenderOptions__MailKitOptions__SecureSocketOptions=0
|
||||
|
||||
# App
|
||||
# App__PrivateKeyFilePath=private.pkcs8.key
|
||||
# App__IdGeneratorGrpcServiceUrl=http://localhost:10002
|
||||
App__DatabaseName=tg
|
||||
App__ReadModelDatabaseName=tg
|
||||
App__CreateTestUsers=False
|
||||
App__AccessHashSecretKey=CHANGE_ME
|
||||
# App__MyTelegramBotOptions__BotApiBaseUrl=http://bot-server:8080
|
||||
# App__MyTelegramBotOptions__Token=
|
||||
# App__MyTelegramBotOptions__WebHookUrl=http://localhost:10004/bot/ProcessBotCommand
|
||||
# App__SubscribeLocalRequest=True
|
||||
# App__SubscribeRemoteRequest=True
|
||||
# App__UseExternalWebHookSender=False
|
||||
# App__WebHookSenderUrl=https://xxx/api/bot/sendWebHook
|
||||
# App=
|
||||
App__ThisDcId=1
|
||||
App__MediaDcId=2
|
||||
# App__UploadRootPath=uploads
|
||||
# App__MediaOnly=False
|
||||
App__Servers__0__Enabled=True
|
||||
# App__Servers__0__ServerType=0
|
||||
# App__Servers__0__Ip=
|
||||
# App__Servers__0__Ipv6=True
|
||||
App__Servers__0__Port=20443
|
||||
# App__Servers__0__Ssl=False
|
||||
# App__Servers__0__CertPemFilePath=
|
||||
# App__Servers__0__KeyPemFilePath=
|
||||
# App__Servers__0__EnableProxyProtocolV2=False
|
||||
# App__Servers__0__MediaOnly=False
|
||||
App__Servers__1__Enabled=True
|
||||
# App__Servers__1__ServerType=0
|
||||
# App__Servers__1__Ip=
|
||||
# App__Servers__1__Ipv6=True
|
||||
App__Servers__1__Port=20543
|
||||
# App__Servers__1__Ssl=False
|
||||
# App__Servers__1__CertPemFilePath=
|
||||
# App__Servers__1__KeyPemFilePath=
|
||||
# App__Servers__1__EnableProxyProtocolV2=False
|
||||
# App__Servers__1__MediaOnly=False
|
||||
# App__Servers__2__Enabled=True
|
||||
# App__Servers__2__ServerType=0
|
||||
# App__Servers__2__Ip=
|
||||
# App__Servers__2__Ipv6=True
|
||||
App__Servers__2__Port=20643
|
||||
# App__Servers__2__Ssl=False
|
||||
# App__Servers__2__CertPemFilePath=
|
||||
# App__Servers__2__KeyPemFilePath=
|
||||
# App__Servers__2__EnableProxyProtocolV2=False
|
||||
# App__Servers__2__MediaOnly=True
|
||||
App__Servers__3__Enabled=True
|
||||
# App__Servers__3__ServerType=0
|
||||
# App__Servers__3__Ip=
|
||||
# App__Servers__3__Ipv6=True
|
||||
App__Servers__3__Port=20644
|
||||
# App__Servers__3__Ssl=False
|
||||
# App__Servers__3__CertPemFilePath=
|
||||
# App__Servers__3__KeyPemFilePath=
|
||||
# App__Servers__3__EnableProxyProtocolV2=False
|
||||
# App__Servers__3__MediaOnly=True
|
||||
App__Servers__4__Enabled=True
|
||||
# App__Servers__4__ServerType=1
|
||||
# App__Servers__4__Ip=
|
||||
# App__Servers__4__Ipv6=True
|
||||
App__Servers__4__Port=30443
|
||||
# App__Servers__4__Ssl=True
|
||||
# App__Servers__4__CertPemFilePath=_wildcard.telegram2.com.pem
|
||||
# App__Servers__4__KeyPemFilePath=_wildcard.telegram2.com-key.pem
|
||||
# App__Servers__4__EnableProxyProtocolV2=False
|
||||
# App__Servers__4__MediaOnly=False
|
||||
App__Servers__5__Enabled=True
|
||||
# App__Servers__5__ServerType=1
|
||||
# App__Servers__5__Ip=
|
||||
# App__Servers__5__Ipv6=True
|
||||
App__Servers__5__Port=30444
|
||||
# App__Servers__5__Ssl=False
|
||||
# App__Servers__5__CertPemFilePath=
|
||||
# App__Servers__5__KeyPemFilePath=
|
||||
# App__Servers__5__EnableProxyProtocolV2=False
|
||||
# App__Servers__5__MediaOnly=False
|
||||
App__QueryServerEventStoreDatabaseName=tg
|
||||
App__QueryServerReadModelDatabaseName=tg
|
||||
App__BotDatabaseName=tg
|
||||
App__FileServerGrpcServiceUrl=http://file-server:8080
|
||||
App__FixedVerifyCode=22222
|
||||
# App__FixedEmailVerificationCode=
|
||||
App__VerificationCodeLength=5
|
||||
App__VerificationCodeExpirationSeconds=300
|
||||
# App__JoinChatDomain=https://t.me
|
||||
# App__ChannelGetDifferenceIntervalSeconds=60
|
||||
# App__UseInMemoryFilters=True
|
||||
# App__IsMediaDc=False
|
||||
# App__AutoCreateSuperGroup=True
|
||||
# App__EnableFutureAuthToken=True
|
||||
# App__SetPremiumToTrueAfterUserCreated=True
|
||||
# App__SendWelcomeMessageAfterUserSignIn=False
|
||||
# App__SetupPasswordRequired=False
|
||||
App__EnableEmailLogin=False
|
||||
# App__SupportUserId=
|
||||
App__CheckPhoneNumberFormat=True
|
||||
App__EnableSearchNonContacts=True
|
||||
# WebRTC / TURN / STUN configuration for voice and video calls
|
||||
# TURN/STUN enabled for voice and video calls
|
||||
App__WebRtcConnections__0__Ip=127.0.0.1
|
||||
App__WebRtcConnections__0__Ipv6=::
|
||||
App__WebRtcConnections__0__Port=3478
|
||||
App__WebRtcConnections__0__Turn=true
|
||||
App__WebRtcConnections__0__Stun=true
|
||||
App__WebRtcConnections__0__UserName=mytelegram
|
||||
App__WebRtcConnections__0__Password=CHANGE_ME
|
||||
TURN_EXTERNAL_IP=127.0.0.1
|
||||
App__DcOptions__0__Id=1
|
||||
App__DcOptions__0__IpAddress=127.0.0.1
|
||||
App__DcOptions__0__Port=20443
|
||||
# App__DcOptions__0__TcpoOnly=True
|
||||
# App__DcOptions__0__ThisPortOnly=True
|
||||
# App__DcOptions__0__Ipv6=False
|
||||
# App__DcOptions__0__Cdn=False
|
||||
# App__DcOptions__0__MediaOnly=False
|
||||
# App__DcOptions__0__Static=False
|
||||
App__DcOptions__1__Id=2
|
||||
App__DcOptions__1__IpAddress=127.0.0.1
|
||||
App__DcOptions__1__Port=20543
|
||||
# App__DcOptions__1__TcpoOnly=True
|
||||
# App__DcOptions__1__ThisPortOnly=True
|
||||
# App__DcOptions__1__Ipv6=False
|
||||
# App__DcOptions__1__Cdn=False
|
||||
# App__DcOptions__1__MediaOnly=False
|
||||
# App__DcOptions__1__Static=False
|
||||
App__DcOptions__2__Id=2
|
||||
App__DcOptions__2__IpAddress=127.0.0.1
|
||||
App__DcOptions__2__Port=20643
|
||||
# App__DcOptions__2__TcpoOnly=True
|
||||
# App__DcOptions__2__ThisPortOnly=True
|
||||
# App__DcOptions__2__Ipv6=False
|
||||
# App__DcOptions__2__Cdn=False
|
||||
# App__DcOptions__2__MediaOnly=True
|
||||
# App__DcOptions__2__Static=False
|
||||
App__DcOptions__0__Enabled=True
|
||||
App__DcOptions__1__Enabled=True
|
||||
App__DcOptions__2__Enabled=True
|
||||
App__DcOptions__3__Enabled=True
|
||||
App__DcOptions__3__Id=2
|
||||
App__DcOptions__3__IpAddress=127.0.0.1
|
||||
App__DcOptions__3__Port=20644
|
||||
# App__DcOptions__3__TcpoOnly=False
|
||||
# App__DcOptions__3__ThisPortOnly=True
|
||||
# App__DcOptions__3__Ipv6=False
|
||||
# App__DcOptions__3__Cdn=False
|
||||
# App__DcOptions__3__MediaOnly=True
|
||||
# App__DcOptions__3__Static=False
|
||||
# App__ServiceAccountKeyJsonFileName=serviceAccountKey.json
|
||||
# App__MessengerServerGrpcServiceUrl=http://messenger-grpc-service:8080
|
||||
# App__DispatchUnknownObjectToMessengerServer=True
|
||||
# App__TempAuthKeyExpirationMinutes=720
|
||||
# App__CommandServerObjectIds=
|
||||
# App__ChannelMemberMaxCountAllowedForPush=100
|
||||
# App__EnablePushNotification=True
|
||||
# App__ForceDownload=False
|
||||
# App__AllowedUserIds__0=2000001
|
||||
# App__AllowedUserIds__1=2000004
|
||||
# App__OfficialTelegramBotOptions__Token=
|
||||
# App__OfficialTelegramBotOptions__ProxyUrl=socks5://localhost:10808
|
||||
# App__OfficialTelegramBotOptions__UseProxy=False
|
||||
# App__LoadAllStickersIntoMemory=True
|
||||
|
||||
# Serilog
|
||||
# Serilog__Using__0=Serilog.Sinks.Console
|
||||
# Serilog__Using__1=Serilog.Sinks.File
|
||||
# Serilog__Using__2=Serilog.Sinks.Async
|
||||
Serilog__MinimumLevel__Default=Information
|
||||
Serilog__MinimumLevel__Override__Microsoft=Warning
|
||||
# Serilog__WriteTo__0__Name=Async
|
||||
# Serilog__WriteTo__0__Args__configure__0__Name=Console
|
||||
# Serilog__WriteTo__0__Args__configure__0__Args__MinimumLevel=Information
|
||||
# Serilog__WriteTo__0__Args__configure__0__Args__outputTemplate=[{Timestamp:yyyy-MM-dd HH:mm:ss:ffffff} [{Level:u3}] {Message}{NewLine}{Exception}
|
||||
# Serilog__WriteTo__0__Args__configure__0__Args__theme=Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console
|
||||
# Serilog__WriteTo__1__Name=Async
|
||||
# Serilog__WriteTo__1__Args__configure__0__Name=File
|
||||
# Serilog__WriteTo__1__Args__configure__0__Args__rollingInterval=Day
|
||||
# Serilog__WriteTo__1__Args__configure__0__Args__path=./Logs/log-.txt
|
||||
# Serilog__WriteTo__1__Args__configure__0__Args__outputTemplate={Timestamp:o} [{Level:u3}] {Message}{NewLine}{Exception}
|
||||
# Serilog__MinimumLevel__Override__Grpc=Debug
|
||||
# Serilog__WriteTo__1__Args__configure__0__Args__MinimumLevel=Debug
|
||||
# Serilog__MinimumLevel__Override__System.Net.Http.HttpClient.Default=Warning
|
||||
# Serilog__MinimumLevel__Override__EventFlow=Information
|
||||
|
||||
# Fragment
|
||||
FRAGMENT_BOT_TOKEN=
|
||||
|
||||
# Database Passwords
|
||||
MONGODB_PASSWORD=CHANGE_ME
|
||||
RABBITMQ_PASSWORD=CHANGE_ME
|
||||
REDIS_PASSWORD=CHANGE_ME
|
||||
MINIO_PASSWORD=CHANGE_ME
|
||||
|
||||
# Admin API Key
|
||||
ADMIN_API_KEY=CHANGE_ME
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env powershell
|
||||
# Полный перезапуск MongoDB с включённой аутентификацией
|
||||
# Скрипт корректно сбрасывает состояние MongoDB и поднимает его заново
|
||||
|
||||
Write-Host "======================================" -ForegroundColor Green
|
||||
Write-Host "MongoDB Authentication Fix - Restart" -ForegroundColor Green
|
||||
Write-Host "======================================" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
$compose_dir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
Set-Location $compose_dir
|
||||
|
||||
Write-Host "[1/5] Stopping all services..." -ForegroundColor Yellow
|
||||
docker-compose down
|
||||
|
||||
Write-Host "[2/5] Removing MongoDB data volume to reinitialize..." -ForegroundColor Yellow
|
||||
if (Test-Path "./data/mongo") {
|
||||
Remove-Item -Recurse -Force "./data/mongo"
|
||||
Write-Host " MongoDB data directory removed" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " MongoDB data directory not found (this is fine)" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
Write-Host "[3/5] Waiting a moment before starting services..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
Write-Host "[4/5] Building and starting services with MongoDB authentication..." -ForegroundColor Yellow
|
||||
docker-compose up --build -d
|
||||
|
||||
Write-Host "[5/5] Waiting for MongoDB to initialize..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 10
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "======================================" -ForegroundColor Green
|
||||
Write-Host "Restart Complete!" -ForegroundColor Green
|
||||
Write-Host "======================================" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Next steps:" -ForegroundColor Cyan
|
||||
Write-Host "1. Check MongoDB initialization: docker-compose logs mongodb"
|
||||
Write-Host "2. Check data-seeder: docker-compose logs data-seeder"
|
||||
Write-Host "3. Check admin-api: docker-compose logs admin-api"
|
||||
Write-Host "4. Monitor bot-api: docker-compose logs bot-api-server"
|
||||
Write-Host ""
|
||||
Write-Host "MongoDB Authentication Details:" -ForegroundColor Cyan
|
||||
Write-Host " Username: admin" -ForegroundColor Gray
|
||||
Write-Host " Auth Database: admin" -ForegroundColor Gray
|
||||
Write-Host " Target Database: tg" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,39 @@
|
||||
# Simple script to create chat themes in MongoDB
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Creating chat themes in MongoDB..." -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
|
||||
# Copy seed script to MongoDB container
|
||||
Write-Host "Copying seed script to container..." -ForegroundColor Gray
|
||||
docker cp "$scriptDir\seed-chat-themes-unicode.js" compose-mongodb-1:/tmp/seed-themes.js
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to copy seed script!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Execute seed script
|
||||
Write-Host "Creating themes..." -ForegroundColor Gray
|
||||
docker exec compose-mongodb-1 mongosh tg /tmp/seed-themes.js
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to create themes!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done! 8 chat themes created successfully!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Themes:" -ForegroundColor Cyan
|
||||
Write-Host " - Home (Blue)" -ForegroundColor White
|
||||
Write-Host " - Love (Pink)" -ForegroundColor White
|
||||
Write-Host " - Party (Orange)" -ForegroundColor White
|
||||
Write-Host " - Ocean (Cyan)" -ForegroundColor White
|
||||
Write-Host " - Flower (Rose)" -ForegroundColor White
|
||||
Write-Host " - Night (Purple)" -ForegroundColor White
|
||||
Write-Host " - Fire (Red)" -ForegroundColor White
|
||||
Write-Host " - Green (Emerald)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,11 @@
|
||||
// Script to delete old StarsSnapshot from MongoDB
|
||||
// Run this in MongoDB shell or via mongosh
|
||||
|
||||
use MyTelegram;
|
||||
|
||||
// Delete all StarsSnapshots with old version
|
||||
db['eventflow.snapshots'].deleteMany({
|
||||
'Metadata.AggregateId': { $regex: '^stars-' }
|
||||
});
|
||||
|
||||
print('Old StarsSnapshots deleted');
|
||||
@@ -0,0 +1,653 @@
|
||||
version: "3.9"
|
||||
|
||||
networks:
|
||||
tg-net:
|
||||
driver: bridge
|
||||
|
||||
services:
|
||||
### Redis
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
#ports:
|
||||
#- "6379:6379"
|
||||
volumes:
|
||||
- ./data/redis:/data
|
||||
|
||||
### RabbitMq
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management-alpine
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
#ports:
|
||||
# - "5672:5672"
|
||||
# - "15672:15672"
|
||||
volumes:
|
||||
- ./data/rabbitmq:/var/lib/rabbitmq
|
||||
environment:
|
||||
# RABBITMQ_DEFAULT_USER: ${RabbitMQ__Connections__Default__UserName}
|
||||
# RABBITMQ_DEFAULT_PASS: ${RabbitMQ__Connections__Default__Password}
|
||||
RABBITMQ_DEFAULT_USER: test
|
||||
RABBITMQ_DEFAULT_PASS: test
|
||||
|
||||
### MongoDb
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
ports:
|
||||
- "27017:27017"
|
||||
volumes:
|
||||
- ./data/mongo/:/data
|
||||
#- ./data/mongo/db:/data/db
|
||||
#- ./data/mongo/configdb:/data/configdb
|
||||
#environment:
|
||||
# MONGO_INITDB_ROOT_USERNAME: admin
|
||||
# MONGO_INITDB_ROOT_PASSWORD: admin
|
||||
|
||||
### Minio
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${Minio__AccessKey}
|
||||
MINIO_ROOT_PASSWORD: ${Minio__SecretKey}
|
||||
volumes:
|
||||
- ./data/minio:/data
|
||||
#- /docker/minio/config:/root/./minio/
|
||||
command: server /data --console-address ":9001"
|
||||
#privileged: true
|
||||
|
||||
################### MyTelegram server
|
||||
|
||||
### Data seeder
|
||||
data-seeder:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.DataSeeder/Dockerfile
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
App__CreateTestUsers: ${App__CreateTestUsers}
|
||||
# App__LoadAllStickersIntoMemory: ${App__LoadAllStickersIntoMemory}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
# RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
volumes:
|
||||
- ./data/mytelegram/data-seeder/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
|
||||
### Admin API
|
||||
admin-api:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.Admin.Api/Dockerfile
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
ports:
|
||||
- "5555:5000"
|
||||
environment:
|
||||
ASPNETCORE_URLS: http://+:5000
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
ADMIN_API_KEY: ${ADMIN_API_KEY}
|
||||
volumes:
|
||||
- ./data/mytelegram/admin-api/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
|
||||
### 1.Gateway server
|
||||
gateway-server:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.GatewayServer/Dockerfile
|
||||
args:
|
||||
BUILD_CONFIGURATION: ${BUILD_CONFIGURATION}
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
ports:
|
||||
- "${App__Servers__0__Port}:${App__Servers__0__Port}"
|
||||
- "${App__Servers__1__Port}:${App__Servers__1__Port}"
|
||||
- "${App__Servers__2__Port}:${App__Servers__2__Port}"
|
||||
- "${App__Servers__3__Port}:${App__Servers__3__Port}"
|
||||
- "${App__Servers__4__Port}:${App__Servers__4__Port}"
|
||||
- "${App__Servers__5__Port}:${App__Servers__5__Port}"
|
||||
volumes:
|
||||
- ./data/mytelegram/gateway/logs:/app/Logs
|
||||
- ./secrets/mtproto:/app/secrets/mtproto:ro
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
App__ThisDcId: ${App__ThisDcId}
|
||||
App__MediaDcId: ${App__MediaDcId}
|
||||
# App__MediaOnly: ${App__MediaOnly}
|
||||
# App__Servers__0__Enabled: ${App__Servers__0__Enabled}
|
||||
# App__Servers__0__ServerType: ${App__Servers__0__ServerType}
|
||||
# App__Servers__0__Ip: ${App__Servers__0__Ip}
|
||||
# App__Servers__0__Ipv6: ${App__Servers__0__Ipv6}
|
||||
App__Servers__0__Port: ${App__Servers__0__Port}
|
||||
# App__Servers__0__Ssl: ${App__Servers__0__Ssl}
|
||||
# App__Servers__0__CertPemFilePath: ${App__Servers__0__CertPemFilePath}
|
||||
# App__Servers__0__KeyPemFilePath: ${App__Servers__0__KeyPemFilePath}
|
||||
# App__Servers__0__EnableProxyProtocolV2: ${App__Servers__0__EnableProxyProtocolV2}
|
||||
# App__Servers__0__MediaOnly: ${App__Servers__0__MediaOnly}
|
||||
App__Servers__1__Enabled: ${App__Servers__1__Enabled}
|
||||
# App__Servers__1__ServerType: ${App__Servers__1__ServerType}
|
||||
# App__Servers__1__Ip: ${App__Servers__1__Ip}
|
||||
# App__Servers__1__Ipv6: ${App__Servers__1__Ipv6}
|
||||
App__Servers__1__Port: ${App__Servers__1__Port}
|
||||
# App__Servers__1__Ssl: ${App__Servers__1__Ssl}
|
||||
# App__Servers__1__CertPemFilePath: ${App__Servers__1__CertPemFilePath}
|
||||
# App__Servers__1__KeyPemFilePath: ${App__Servers__1__KeyPemFilePath}
|
||||
# App__Servers__1__EnableProxyProtocolV2: ${App__Servers__1__EnableProxyProtocolV2}
|
||||
# App__Servers__1__MediaOnly: ${App__Servers__1__MediaOnly}
|
||||
App__Servers__2__Enabled: ${App__Servers__2__Enabled}
|
||||
# App__Servers__2__ServerType: ${App__Servers__2__ServerType}
|
||||
# App__Servers__2__Ip: ${App__Servers__2__Ip}
|
||||
# App__Servers__2__Ipv6: ${App__Servers__2__Ipv6}
|
||||
App__Servers__2__Port: ${App__Servers__2__Port}
|
||||
# App__Servers__2__Ssl: ${App__Servers__2__Ssl}
|
||||
# App__Servers__2__CertPemFilePath: ${App__Servers__2__CertPemFilePath}
|
||||
# App__Servers__2__KeyPemFilePath: ${App__Servers__2__KeyPemFilePath}
|
||||
# App__Servers__2__EnableProxyProtocolV2: ${App__Servers__2__EnableProxyProtocolV2}
|
||||
# App__Servers__2__MediaOnly: ${App__Servers__2__MediaOnly}
|
||||
App__Servers__3__Enabled: ${App__Servers__3__Enabled}
|
||||
# App__Servers__3__ServerType: ${App__Servers__3__ServerType}
|
||||
# App__Servers__3__Ip: ${App__Servers__3__Ip}
|
||||
# App__Servers__3__Ipv6: ${App__Servers__3__Ipv6}
|
||||
App__Servers__3__Port: ${App__Servers__3__Port}
|
||||
# App__Servers__3__Ssl: ${App__Servers__3__Ssl}
|
||||
# App__Servers__3__CertPemFilePath: ${App__Servers__3__CertPemFilePath}
|
||||
# App__Servers__3__KeyPemFilePath: ${App__Servers__3__KeyPemFilePath}
|
||||
# App__Servers__3__EnableProxyProtocolV2: ${App__Servers__3__EnableProxyProtocolV2}
|
||||
# App__Servers__3__MediaOnly: ${App__Servers__3__MediaOnly}
|
||||
# App__Servers__4__Enabled: ${App__Servers__4__Enabled}
|
||||
# App__Servers__4__ServerType: ${App__Servers__4__ServerType}
|
||||
# App__Servers__4__Ip: ${App__Servers__4__Ip}
|
||||
# App__Servers__4__Ipv6: ${App__Servers__4__Ipv6}
|
||||
App__Servers__4__Port: ${App__Servers__4__Port}
|
||||
# App__Servers__4__Ssl: ${App__Servers__4__Ssl}
|
||||
# App__Servers__4__CertPemFilePath: ${App__Servers__4__CertPemFilePath}
|
||||
# App__Servers__4__KeyPemFilePath: ${App__Servers__4__KeyPemFilePath}
|
||||
# App__Servers__4__EnableProxyProtocolV2: ${App__Servers__4__EnableProxyProtocolV2}
|
||||
# App__Servers__4__MediaOnly: ${App__Servers__4__MediaOnly}
|
||||
# App__Servers__5__Enabled: ${App__Servers__5__Enabled}
|
||||
# App__Servers__5__ServerType: ${App__Servers__5__ServerType}
|
||||
# App__Servers__5__Ip: ${App__Servers__5__Ip}
|
||||
# App__Servers__5__Ipv6: ${App__Servers__5__Ipv6}
|
||||
App__Servers__5__Port: ${App__Servers__5__Port}
|
||||
# App__Servers__5__Ssl: ${App__Servers__5__Ssl}
|
||||
# App__Servers__5__CertPemFilePath: ${App__Servers__5__CertPemFilePath}
|
||||
# App__Servers__5__KeyPemFilePath: ${App__Servers__5__KeyPemFilePath}
|
||||
# App__Servers__5__EnableProxyProtocolV2: ${App__Servers__5__EnableProxyProtocolV2}
|
||||
# App__Servers__5__MediaOnly: ${App__Servers__5__MediaOnly}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
- mongodb
|
||||
- redis
|
||||
|
||||
# ### 2.Seq server
|
||||
# seq-server:
|
||||
# image: mytelegram/mytelegram-seq-server:${MyTelegramVersion}
|
||||
# restart: always
|
||||
# networks:
|
||||
# - tg-net
|
||||
# #ports:
|
||||
# # - "10001:80"
|
||||
# environment:
|
||||
# ConnectionStrings__Default: ${DefaultConnectionStrings}
|
||||
# Serilog__MinimumLevel__Default: ${DefaultLogLevel}
|
||||
# volumes:
|
||||
# - ./data/mytelegram/seq/logs:/data/Logs
|
||||
# depends_on:
|
||||
# - mongodb
|
||||
|
||||
### 2.Auth server
|
||||
auth-server:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.AuthServer/Dockerfile
|
||||
args:
|
||||
BUILD_CONFIGURATION: ${BUILD_CONFIGURATION}
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
App__PrivateKeyFilePath: /app/secrets/mtproto/rsa_private.pem
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
Redis__Configuration: ${Redis__Configuration}
|
||||
volumes:
|
||||
- ./data/mytelegram/auth/logs:/app/Logs
|
||||
- ./secrets/mtproto:/app/secrets/mtproto:ro
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
### 3.Session server
|
||||
session-server:
|
||||
image: ${MyTelegramRegistry}/mytelegram-session-server:${MyTelegramVersion}
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
# App__ThisDcId: ${App__ThisDcId}
|
||||
# App__MediaOnly: ${App__MediaOnly}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
App__AccessHashSecretKey: ${App__AccessHashSecretKey}
|
||||
# App__IdGeneratorGrpcServiceUrl: ${App__IdGeneratorGrpcServiceUrl}
|
||||
App__MessengerServerGrpcServiceUrl: ${App__MessengerServerGrpcServiceUrl}
|
||||
# App__DispatchUnknownObjectToMessengerServer: ${App__DispatchUnknownObjectToMessengerServer}
|
||||
# App__TempAuthKeyExpirationMinutes: ${App__TempAuthKeyExpirationMinutes}
|
||||
# App__CommandServerObjectIds: ${App__CommandServerObjectIds}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
# RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
Redis__Configuration: ${Redis__Configuration}
|
||||
volumes:
|
||||
- ./data/mytelegram/session/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
# ### 5.Messenger grpc server
|
||||
# messenger-grpc-service:
|
||||
# image: mytelegram/mytelegram-messenger-grpc-service:${MyTelegramVersion}
|
||||
# restart: always
|
||||
# networks:
|
||||
# - tg-net
|
||||
# environment:
|
||||
# Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
# ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
# App__DatabaseName: tg
|
||||
# App__ReadModelDatabaseName: tg
|
||||
# volumes:
|
||||
# - ./data/mytelegram/messenger-grpc-service/logs:/app/Logs
|
||||
# depends_on:
|
||||
# - mongodb
|
||||
|
||||
### 4.File server (original - for Telegram)
|
||||
file-server:
|
||||
image: ${MyTelegramRegistry}/mytelegram-file-server:${MyTelegramVersion}
|
||||
restart: always
|
||||
user: "0:0" # Run as root to avoid permission issues
|
||||
networks:
|
||||
- tg-net
|
||||
volumes:
|
||||
- ./data/mytelegram/file/uploads:/app/uploads
|
||||
- ./data/mytelegram/file:/app/Logs
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
App__AccessHashSecretKey: ${App__AccessHashSecretKey}
|
||||
Minio__Endpoint: ${Minio__Endpoint}
|
||||
Minio__AccessKey: ${Minio__AccessKey}
|
||||
Minio__SecretKey: ${Minio__SecretKey}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
Redis__Configuration: ${Redis__Configuration}
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
|
||||
### 4.5 File merge proxy (HTTP API for merging file parts from local filesystem)
|
||||
file-merge-proxy:
|
||||
build:
|
||||
context: ../../file-merge-proxy
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
volumes:
|
||||
- ./data/mytelegram/file/uploads:/app/uploads
|
||||
environment:
|
||||
PORT: 5000
|
||||
UPLOAD_PATH: /app/uploads
|
||||
ports:
|
||||
- "5000:5000"
|
||||
|
||||
### 5.Messenger command server
|
||||
messenger-command-server:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.Messenger.CommandServer/Dockerfile
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
App__AccessHashSecretKey: ${App__AccessHashSecretKey}
|
||||
App__FileServerGrpcServiceUrl: ${App__FileServerGrpcServiceUrl}
|
||||
App__FixedVerifyCode: ${App__FixedVerifyCode}
|
||||
App__VerificationCodeLength: ${App__VerificationCodeLength}
|
||||
App__VerificationCodeExpirationSeconds: ${App__VerificationCodeExpirationSeconds}
|
||||
App__CheckPhoneNumberFormat: ${App__CheckPhoneNumberFormat}
|
||||
App__EnableSearchNonContacts: ${App__EnableSearchNonContacts}
|
||||
# App__JoinChatDomain: ${App__JoinChatDomain}
|
||||
# App__ChannelGetDifferenceIntervalSeconds: ${App__ChannelGetDifferenceIntervalSeconds}
|
||||
# App__UseInMemoryFilters: ${App__UseInMemoryFilters}
|
||||
# App__IsMediaDc: ${App__IsMediaDc}
|
||||
# App__AutoCreateSuperGroup: ${App__AutoCreateSuperGroup}
|
||||
# App__EnableFutureAuthToken: ${App__EnableFutureAuthToken}
|
||||
# App__SetPremiumToTrueAfterUserCreated: ${App__SetPremiumToTrueAfterUserCreated}
|
||||
# App__SendWelcomeMessageAfterUserSignIn: ${App__SendWelcomeMessageAfterUserSignIn}
|
||||
App__WebRtcConnections__0__Ip: ${App__WebRtcConnections__0__Ip}
|
||||
App__WebRtcConnections__0__Ipv6: ${App__WebRtcConnections__0__Ipv6}
|
||||
App__WebRtcConnections__0__Port: ${App__WebRtcConnections__0__Port}
|
||||
App__WebRtcConnections__0__Turn: ${App__WebRtcConnections__0__Turn}
|
||||
App__WebRtcConnections__0__Stun: ${App__WebRtcConnections__0__Stun}
|
||||
App__WebRtcConnections__0__UserName: ${App__WebRtcConnections__0__UserName}
|
||||
App__WebRtcConnections__0__Password: ${App__WebRtcConnections__0__Password}
|
||||
# App__ThisDcId: ${App__ThisDcId}
|
||||
# App__DcOptions__0__Enabled: ${App__DcOptions__0__Enabled}
|
||||
# App__DcOptions__0__Id: ${App__DcOptions__0__Id}
|
||||
App__DcOptions__0__IpAddress: ${App__DcOptions__0__IpAddress}
|
||||
# App__DcOptions__0__Port: ${App__DcOptions__0__Port}
|
||||
# App__DcOptions__0__TcpoOnly: ${App__DcOptions__0__TcpoOnly}
|
||||
# App__DcOptions__0__ThisPortOnly: ${App__DcOptions__0__ThisPortOnly}
|
||||
# App__DcOptions__0__Ipv6: ${App__DcOptions__0__Ipv6}
|
||||
# App__DcOptions__0__Cdn: ${App__DcOptions__0__Cdn}
|
||||
# App__DcOptions__0__MediaOnly: ${App__DcOptions__0__MediaOnly}
|
||||
# App__DcOptions__0__Static: ${App__DcOptions__0__Static}
|
||||
|
||||
App__DcOptions__1__Enabled: ${App__DcOptions__1__Enabled}
|
||||
# App__DcOptions__1__Id: ${App__DcOptions__1__Id}
|
||||
App__DcOptions__1__IpAddress: ${App__DcOptions__1__IpAddress}
|
||||
App__DcOptions__1__Port: ${App__DcOptions__1__Port}
|
||||
# App__DcOptions__1__TcpoOnly: ${App__DcOptions__1__TcpoOnly}
|
||||
# App__DcOptions__1__ThisPortOnly: ${App__DcOptions__1__ThisPortOnly}
|
||||
# App__DcOptions__1__Ipv6: ${App__DcOptions__1__Ipv6}
|
||||
# App__DcOptions__1__Cdn: ${App__DcOptions__1__Cdn}
|
||||
# App__DcOptions__1__MediaOnly: ${App__DcOptions__1__MediaOnly}
|
||||
# App__DcOptions__1__Static: ${App__DcOptions__1__Static}
|
||||
|
||||
App__DcOptions__2__Enabled: ${App__DcOptions__2__Enabled}
|
||||
# App__DcOptions__2__Id: ${App__DcOptions__2__Id}
|
||||
App__DcOptions__2__IpAddress: ${App__DcOptions__2__IpAddress}
|
||||
# App__DcOptions__2__Port: ${App__DcOptions__2__Port}
|
||||
# App__DcOptions__2__TcpoOnly: ${App__DcOptions__2__TcpoOnly}
|
||||
# App__DcOptions__2__ThisPortOnly: ${App__DcOptions__2__ThisPortOnly}
|
||||
# App__DcOptions__2__Ipv6: ${App__DcOptions__2__Ipv6}
|
||||
# App__DcOptions__2__Cdn: ${App__DcOptions__2__Cdn}
|
||||
# App__DcOptions__2__MediaOnly: ${App__DcOptions__2__MediaOnly}
|
||||
# App__DcOptions__2__Static: ${App__DcOptions__2__Static}
|
||||
|
||||
App__DcOptions__3__Enabled: ${App__DcOptions__3__Enabled}
|
||||
# App__DcOptions__3__Id: ${App__DcOptions__3__Id}
|
||||
App__DcOptions__3__IpAddress: ${App__DcOptions__3__IpAddress}
|
||||
# App__DcOptions__3__Port: ${App__DcOptions__3__Port}
|
||||
# App__DcOptions__3__TcpoOnly: ${App__DcOptions__3__TcpoOnly}
|
||||
# App__DcOptions__3__ThisPortOnly: ${App__DcOptions__3__ThisPortOnly}
|
||||
# App__DcOptions__3__Ipv6: ${App__DcOptions__3__Ipv6}
|
||||
# App__DcOptions__3__Cdn: ${App__DcOptions__3__Cdn}
|
||||
# App__DcOptions__3__MediaOnly: ${App__DcOptions__3__MediaOnly}
|
||||
# App__DcOptions__3__Static: ${App__DcOptions__3__Static}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
# RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
Redis__Configuration: ${Redis__Configuration}
|
||||
volumes:
|
||||
- ./data/mytelegram/file/uploads:/app/uploads
|
||||
- ./data/mytelegram/command-server/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
### 6.Messenger query server
|
||||
messenger-query-server:
|
||||
# CHANGED: Build locally from source instead of using remote image
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.Messenger.QueryServer/Dockerfile
|
||||
image: mytelegram-messenger-query-server-local:latest
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
App__AccessHashSecretKey: ${App__AccessHashSecretKey}
|
||||
App__FileServerGrpcServiceUrl: ${App__FileServerGrpcServiceUrl}
|
||||
App__FixedVerifyCode: ${App__FixedVerifyCode}
|
||||
App__VerificationCodeLength: ${App__VerificationCodeLength}
|
||||
App__VerificationCodeExpirationSeconds: ${App__VerificationCodeExpirationSeconds}
|
||||
App__EnableSearchNonContacts: ${App__EnableSearchNonContacts}
|
||||
# App__JoinChatDomain: ${App__JoinChatDomain}
|
||||
# App__ChannelGetDifferenceIntervalSeconds: ${App__ChannelGetDifferenceIntervalSeconds}
|
||||
# App__UseInMemoryFilters: ${App__UseInMemoryFilters}
|
||||
# App__IsMediaDc: ${App__IsMediaDc}
|
||||
# App__AutoCreateSuperGroup: ${App__AutoCreateSuperGroup}
|
||||
# App__EnableFutureAuthToken: ${App__EnableFutureAuthToken}
|
||||
# App__SetPremiumToTrueAfterUserCreated: ${App__SetPremiumToTrueAfterUserCreated}
|
||||
# App__SendWelcomeMessageAfterUserSignIn: ${App__SendWelcomeMessageAfterUserSignIn}
|
||||
App__WebRtcConnections__0__Ip: ${App__WebRtcConnections__0__Ip}
|
||||
App__WebRtcConnections__0__Ipv6: ${App__WebRtcConnections__0__Ipv6}
|
||||
App__WebRtcConnections__0__Port: ${App__WebRtcConnections__0__Port}
|
||||
App__WebRtcConnections__0__Turn: ${App__WebRtcConnections__0__Turn}
|
||||
App__WebRtcConnections__0__Stun: ${App__WebRtcConnections__0__Stun}
|
||||
App__WebRtcConnections__0__UserName: ${App__WebRtcConnections__0__UserName}
|
||||
App__WebRtcConnections__0__Password: ${App__WebRtcConnections__0__Password}
|
||||
# App__ThisDcId: ${App__ThisDcId}
|
||||
# App__DcOptions__0__Enabled: ${App__DcOptions__0__Enabled}
|
||||
# App__DcOptions__0__Id: ${App__DcOptions__0__Id}
|
||||
App__DcOptions__0__IpAddress: ${App__DcOptions__0__IpAddress}
|
||||
App__DcOptions__0__Port: ${App__DcOptions__0__Port}
|
||||
# App__DcOptions__0__TcpoOnly: ${App__DcOptions__0__TcpoOnly}
|
||||
# App__DcOptions__0__ThisPortOnly: ${App__DcOptions__0__ThisPortOnly}
|
||||
# App__DcOptions__0__Ipv6: ${App__DcOptions__0__Ipv6}
|
||||
# App__DcOptions__0__Cdn: ${App__DcOptions__0__Cdn}
|
||||
# App__DcOptions__0__MediaOnly: ${App__DcOptions__0__MediaOnly}
|
||||
# App__DcOptions__0__Static: ${App__DcOptions__0__Static}
|
||||
|
||||
App__DcOptions__1__Enabled: ${App__DcOptions__1__Enabled}
|
||||
# App__DcOptions__1__Id: ${App__DcOptions__1__Id}
|
||||
App__DcOptions__1__IpAddress: ${App__DcOptions__1__IpAddress}
|
||||
App__DcOptions__1__Port: ${App__DcOptions__1__Port}
|
||||
# App__DcOptions__1__TcpoOnly: ${App__DcOptions__1__TcpoOnly}
|
||||
# App__DcOptions__1__ThisPortOnly: ${App__DcOptions__1__ThisPortOnly}
|
||||
# App__DcOptions__1__Ipv6: ${App__DcOptions__1__Ipv6}
|
||||
# App__DcOptions__1__Cdn: ${App__DcOptions__1__Cdn}
|
||||
# App__DcOptions__1__MediaOnly: ${App__DcOptions__1__MediaOnly}
|
||||
# App__DcOptions__1__Static: ${App__DcOptions__1__Static}
|
||||
|
||||
App__DcOptions__2__Enabled: ${App__DcOptions__2__Enabled}
|
||||
# App__DcOptions__2__Id: ${App__DcOptions__2__Id}
|
||||
App__DcOptions__2__IpAddress: ${App__DcOptions__2__IpAddress}
|
||||
App__DcOptions__2__Port: ${App__DcOptions__2__Port}
|
||||
# App__DcOptions__2__TcpoOnly: ${App__DcOptions__2__TcpoOnly}
|
||||
# App__DcOptions__2__ThisPortOnly: ${App__DcOptions__2__ThisPortOnly}
|
||||
# App__DcOptions__2__Ipv6: ${App__DcOptions__2__Ipv6}
|
||||
# App__DcOptions__2__Cdn: ${App__DcOptions__2__Cdn}
|
||||
# App__DcOptions__2__MediaOnly: ${App__DcOptions__2__MediaOnly}
|
||||
# App__DcOptions__2__Static: ${App__DcOptions__2__Static}
|
||||
|
||||
App__DcOptions__3__Enabled: ${App__DcOptions__3__Enabled}
|
||||
# App__DcOptions__3__Id: ${App__DcOptions__3__Id}
|
||||
App__DcOptions__3__IpAddress: ${App__DcOptions__3__IpAddress}
|
||||
# App__DcOptions__3__Port: ${App__DcOptions__3__Port}
|
||||
# App__DcOptions__3__TcpoOnly: ${App__DcOptions__3__TcpoOnly}
|
||||
# App__DcOptions__3__ThisPortOnly: ${App__DcOptions__3__ThisPortOnly}
|
||||
# App__DcOptions__3__Ipv6: ${App__DcOptions__3__Ipv6}
|
||||
# App__DcOptions__3__Cdn: ${App__DcOptions__3__Cdn}
|
||||
# App__DcOptions__3__MediaOnly: ${App__DcOptions__3__MediaOnly}
|
||||
# App__DcOptions__3__Static: ${App__DcOptions__3__Static}
|
||||
Minio__Endpoint: ${Minio__Endpoint}
|
||||
Minio__AccessKey: ${Minio__AccessKey}
|
||||
Minio__SecretKey: ${Minio__SecretKey}
|
||||
Minio__BucketName: ${Minio__BucketName}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
# RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
Redis__Configuration: ${Redis__Configuration}
|
||||
volumes:
|
||||
- ./data/mytelegram/query-server/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
### 7.Bot API Server
|
||||
bot-api-server:
|
||||
build:
|
||||
context: ../../source
|
||||
dockerfile: src/MyTelegram.BotApi/Dockerfile
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
ASPNETCORE_URLS: http://+:8081
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
ConnectionStrings__Default: ${ConnectionStrings__Default}
|
||||
App__DatabaseName: tg
|
||||
App__ReadModelDatabaseName: tg
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
RabbitMQ__EventBus__ClientProvidedName: MyTelegram.BotApi
|
||||
RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
MongoDB__ConnectionString: ${ConnectionStrings__Default}
|
||||
MongoDB__DatabaseName: tg
|
||||
BotApi__Port: 8081
|
||||
BotApi__LocalMode: true
|
||||
BotApi__MaxFileSize: 2147483648
|
||||
volumes:
|
||||
- ./data/mytelegram/bot-api/logs:/app/Logs
|
||||
depends_on:
|
||||
- mongodb
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
### 8.TURN/STUN server for VoIP
|
||||
turn-server:
|
||||
image: coturn/coturn:latest
|
||||
restart: always
|
||||
network_mode: host
|
||||
# networks:
|
||||
# - tg-net
|
||||
# ports:
|
||||
# - "3478:3478/udp"
|
||||
# - "3478:3478/tcp"
|
||||
# - "5349:5349/tcp"
|
||||
# Note: network_mode: host exposes all ports directly, including relay ports 49152-65535
|
||||
volumes:
|
||||
- ./turnserver.conf:/etc/coturn/turnserver.conf:ro
|
||||
- ./data/coturn/logs:/var/log/coturn
|
||||
environment:
|
||||
TURN_EXTERNAL_IP: "${TURN_EXTERNAL_IP:-auto-detect}"
|
||||
command: ["-c", "/etc/coturn/turnserver.conf"]
|
||||
|
||||
### 9.Sms sender
|
||||
sms-sender:
|
||||
image: ${MyTelegramRegistry}/mytelegram-sms-sender:${MyTelegramVersion}
|
||||
restart: always
|
||||
networks:
|
||||
- tg-net
|
||||
environment:
|
||||
Serilog__MinimumLevel__Default: ${Serilog__MinimumLevel__Default}
|
||||
# TwilioSms__Enabled: ${TwilioSms__Enabled}
|
||||
# TwilioSms__AccountSId: ${TwilioSms__AccountSId}
|
||||
# TwilioSms__AuthToken: ${TwilioSms__AuthToken}
|
||||
# TwilioSms__FromNumber: ${TwilioSms__FromNumber}
|
||||
# TwilioSms__MessagingServiceSId: ${TwilioSms__MessagingServiceSId}
|
||||
# VonageSms__Enabled: ${VonageSms__Enabled}
|
||||
# VonageSms__BrandName: ${VonageSms__BrandName}
|
||||
# VonageSms__ApiKey: ${VonageSms__ApiKey}
|
||||
# VonageSms__ApiSecret: ${VonageSms__ApiSecret}
|
||||
RabbitMQ__Connections__Default__HostName: ${RabbitMQ__Connections__Default__HostName}
|
||||
RabbitMQ__Connections__Default__Port: ${RabbitMQ__Connections__Default__Port}
|
||||
RabbitMQ__Connections__Default__UserName: ${RabbitMQ__Connections__Default__UserName}
|
||||
RabbitMQ__Connections__Default__Password: ${RabbitMQ__Connections__Default__Password}
|
||||
# RabbitMQ__EventBus__ClientName: ${RabbitMQ__EventBus__ClientName}
|
||||
# RabbitMQ__EventBus__ExchangeName: ${RabbitMQ__EventBus__ExchangeName}
|
||||
volumes:
|
||||
- ./data/mytelegram/sms-sender/logs:/app/Logs
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
|
||||
### 10.Fragment Backend API (Run locally with npm run dev)
|
||||
# fragment-backend:
|
||||
# build:
|
||||
# context: ../../fragment/fragment-backend
|
||||
# dockerfile: Dockerfile
|
||||
# restart: always
|
||||
# networks:
|
||||
# - tg-net
|
||||
# ports:
|
||||
# - "3001:3001"
|
||||
# environment:
|
||||
# PORT: 3001
|
||||
# MONGODB_URI: mongodb://mongodb:27017/tg
|
||||
# JWT_SECRET: fragment_secret_key_change_in_production
|
||||
# BOT_API_URL: http://bot-api-server:8081
|
||||
# FRAGMENT_BOT_TOKEN: ${FRAGMENT_BOT_TOKEN}
|
||||
# volumes:
|
||||
# - ./data/fragment/backend/logs:/app/logs
|
||||
# depends_on:
|
||||
# - mongodb
|
||||
# - bot-api-server
|
||||
@@ -0,0 +1,31 @@
|
||||
// Скрипт инициализации MongoDB
|
||||
// Запускается при старте MongoDB с включённой аутентификацией
|
||||
// Гарантирует, что пользователь admin существует с нужными правами
|
||||
|
||||
// Учтите: скрипт выполняется в контексте базы admin
|
||||
|
||||
// Создаём пользователя admin, если его ещё нет
|
||||
try {
|
||||
db.createUser({
|
||||
user: "admin",
|
||||
pwd: "CHANGE_ME",
|
||||
roles: [
|
||||
{ role: "root", db: "admin" },
|
||||
{ role: "dbOwner", db: "tg" },
|
||||
{ role: "readWrite", db: "tg" }
|
||||
]
|
||||
});
|
||||
print("Пользователь admin создан");
|
||||
} catch (e) {
|
||||
if (e.code === 48) {
|
||||
// Пользователь уже существует - это нормально
|
||||
print("Пользователь admin уже существует");
|
||||
} else {
|
||||
print("Ошибка при создании пользователя admin: " + e.message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Создаём базу tg и обеспечиваем доступ к ней
|
||||
db.getSiblingDB("tg").createCollection("dummy", { capped: false });
|
||||
print("База 'tg' готова");
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
echo Stopping session-server container...
|
||||
docker-compose stop session-server
|
||||
|
||||
echo Removing session-server container...
|
||||
docker-compose rm -f session-server
|
||||
|
||||
echo Removing old session server image (0.33.211.906)...
|
||||
docker rmi mytelegram/mytelegram-session-server:0.33.211.906
|
||||
|
||||
echo Pulling session server image v0.32.206.802...
|
||||
docker pull mytelegram/mytelegram-session-server:0.32.206.802
|
||||
|
||||
echo Starting session-server with version 0.32.206.802...
|
||||
set MyTelegramVersion=0.32.206.802
|
||||
docker-compose up -d session-server
|
||||
|
||||
echo Done.
|
||||
pause
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Полный перезапуск MongoDB с включённой аутентификацией
|
||||
# Скрипт корректно сбрасывает состояние MongoDB и поднимает его заново
|
||||
|
||||
echo "======================================"
|
||||
echo "MongoDB Authentication Fix - Restart"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "[1/5] Stopping all services..."
|
||||
docker-compose down
|
||||
|
||||
echo "[2/5] Removing MongoDB data volume to reinitialize..."
|
||||
if [ -d "./data/mongo" ]; then
|
||||
rm -rf "./data/mongo"
|
||||
echo " MongoDB data directory removed"
|
||||
else
|
||||
echo " MongoDB data directory not found (this is fine)"
|
||||
fi
|
||||
|
||||
echo "[3/5] Waiting a moment before starting services..."
|
||||
sleep 2
|
||||
|
||||
echo "[4/5] Building and starting services with MongoDB authentication..."
|
||||
docker-compose up --build -d
|
||||
|
||||
echo "[5/5] Waiting for MongoDB to initialize..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo "Restart Complete!"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Check MongoDB initialization: docker-compose logs mongodb"
|
||||
echo "2. Check data-seeder: docker-compose logs data-seeder"
|
||||
echo "3. Check admin-api: docker-compose logs admin-api"
|
||||
echo "4. Monitor bot-api: docker-compose logs bot-api-server"
|
||||
echo ""
|
||||
echo "MongoDB Authentication Details:"
|
||||
echo " Username: admin"
|
||||
echo " Auth Database: admin"
|
||||
echo " Target Database: tg"
|
||||
echo ""
|
||||
@@ -0,0 +1,23 @@
|
||||
# RSA-ключи MTProto
|
||||
|
||||
Здесь должны лежать RSA-ключи сервера. Приватные ключи намеренно не хранятся в
|
||||
репозитории — сгенерируйте свою пару перед запуском.
|
||||
|
||||
```bash
|
||||
# Приватный ключ (PKCS#8)
|
||||
openssl genrsa -out rsa_private.pem 2048
|
||||
|
||||
# Приватный ключ в формате PKCS#1 (используется сервером)
|
||||
openssl rsa -in rsa_private.pem -traditional -out rsa_private_pkcs1.pem
|
||||
|
||||
# Публичный ключ
|
||||
openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
|
||||
```
|
||||
|
||||
После генерации в этом каталоге должны появиться файлы:
|
||||
|
||||
- `rsa_private.pem`
|
||||
- `rsa_private_pkcs1.pem`
|
||||
- `rsa_public.pem`
|
||||
|
||||
Эти файлы не коммитьте.
|
||||
@@ -0,0 +1,31 @@
|
||||
// Скрипт MongoDB: добавляет упрощённые темы оформления чата (только эмодзи-маркер)
|
||||
// Сделано по образцу официального конструктора chatTheme из Telegram
|
||||
// Запуск: docker exec -i compose-mongodb-1 mongosh tg
|
||||
|
||||
db = db.getSiblingDB('tg');
|
||||
|
||||
// Проверяем, не созданы ли темы оформления ранее
|
||||
const existingCount = db.chat_themes.countDocuments();
|
||||
if (existingCount > 0) {
|
||||
print(`Темы оформления уже есть (${existingCount}), удаляем...`);
|
||||
db.chat_themes.deleteMany({});
|
||||
}
|
||||
|
||||
print('Добавляем упрощённые темы оформления чата (только эмодзи-маркер)...');
|
||||
|
||||
// Официальные темы оформления Telegram - только эмодзи-маркеры
|
||||
const themes = [
|
||||
{ _id: "1", Emoticon: "🏠" }, // Home
|
||||
{ _id: "2", Emoticon: "❤️" }, // Love
|
||||
{ _id: "3", Emoticon: "🎉" }, // Party
|
||||
{ _id: "4", Emoticon: "🌊" }, // Ocean
|
||||
{ _id: "5", Emoticon: "🌸" }, // Flower
|
||||
{ _id: "6", Emoticon: "🌙" }, // Night
|
||||
{ _id: "7", Emoticon: "🔥" }, // Fire
|
||||
{ _id: "8", Emoticon: "💚" } // Green
|
||||
];
|
||||
|
||||
db.chat_themes.insertMany(themes);
|
||||
|
||||
print(`Добавлено упрощённых тем оформления чата: ${themes.length}`);
|
||||
print(`Total chat themes in database: ${db.chat_themes.countDocuments()}`);
|
||||
@@ -0,0 +1,31 @@
|
||||
// Скрипт MongoDB: добавляет темы оформления чата, используя Unicode-escape-последовательности
|
||||
// Так удаётся избежать проблем с кодировкой при передаче через docker exec
|
||||
// Запуск: docker exec -i compose-mongodb-1 mongosh tg < seed-chat-themes-unicode.js
|
||||
|
||||
db = db.getSiblingDB('tg');
|
||||
|
||||
// Удаляем существующие темы
|
||||
print('Удаляем существующие темы оформления чата...');
|
||||
db.chat_themes.deleteMany({});
|
||||
|
||||
print('Добавляем темы оформления через Unicode-escape...');
|
||||
|
||||
// Используем Unicode-escape-последовательности, чтобы избежать проблем с кодировкой
|
||||
const themes = [
|
||||
{ _id: "1", Emoticon: "\uD83C\uDFE0" }, // Home
|
||||
{ _id: "2", Emoticon: "\u2764\uFE0F" }, // Love
|
||||
{ _id: "3", Emoticon: "\uD83C\uDF89" }, // Party
|
||||
{ _id: "4", Emoticon: "\uD83C\uDF0A" }, // Ocean
|
||||
{ _id: "5", Emoticon: "\uD83C\uDF38" }, // Flower
|
||||
{ _id: "6", Emoticon: "\uD83C\uDF19" }, // Night
|
||||
{ _id: "7", Emoticon: "\uD83D\uDD25" }, // Fire
|
||||
{ _id: "8", Emoticon: "\uD83D\uDC9A" } // Green
|
||||
];
|
||||
|
||||
db.chat_themes.insertMany(themes);
|
||||
|
||||
print(`Добавлено тем оформления чата: ${themes.length}`);
|
||||
print('Проверяем темы:');
|
||||
db.chat_themes.find().forEach(function(theme) {
|
||||
print(` ID: ${theme._id}, Emoticon: ${theme.Emoticon}`);
|
||||
});
|
||||
@@ -0,0 +1,302 @@
|
||||
// Скрипт MongoDB: добавляет стандартные темы оформления чата вместе с обоями
|
||||
// Запуск: docker exec -i compose-mongodb-1 mongosh tg
|
||||
|
||||
db = db.getSiblingDB('tg');
|
||||
|
||||
// Проверяем, не созданы ли темы оформления ранее
|
||||
const existingCount = db.chat_themes.countDocuments();
|
||||
if (existingCount > 0) {
|
||||
print(`Chat themes already exist (${existingCount}), skipping...`);
|
||||
} else {
|
||||
print('Добавляем стандартные темы оформления чата...');
|
||||
|
||||
const themes = [
|
||||
{
|
||||
_id: "1",
|
||||
Id: NumberLong("1"),
|
||||
AccessHash: NumberLong("1234567890123456"),
|
||||
Slug: "blue",
|
||||
Title: "Blue",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "💙",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: false,
|
||||
BaseTheme: NumberLong("2"), // Дневная
|
||||
AccentColor: 0x0f4c81,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0x0f4c81, 0x1a7fc1],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1001"),
|
||||
AccessHash: NumberLong("1001"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "blue-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0x0f4c81,
|
||||
SecondBackgroundColor: 0x1a7fc1,
|
||||
ThirdBackgroundColor: null,
|
||||
FourthBackgroundColor: null,
|
||||
Intensity: 50,
|
||||
Rotation: 45,
|
||||
Emoticon: "💙"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "2",
|
||||
Id: NumberLong("2"),
|
||||
AccessHash: NumberLong("1234567890123457"),
|
||||
Slug: "pink",
|
||||
Title: "Pink",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "💗",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: false,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0xff6b9d,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0xff6b9d, 0xffa8c5],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1002"),
|
||||
AccessHash: NumberLong("1002"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "pink-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0xff6b9d,
|
||||
SecondBackgroundColor: 0xffa8c5,
|
||||
ThirdBackgroundColor: null,
|
||||
FourthBackgroundColor: null,
|
||||
Intensity: 50,
|
||||
Rotation: 135,
|
||||
Emoticon: "💗"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "3",
|
||||
Id: NumberLong("3"),
|
||||
AccessHash: NumberLong("1234567890123458"),
|
||||
Slug: "green",
|
||||
Title: "Green",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "💚",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: false,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0x0a8754,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0x0a8754, 0x2ecc71],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1003"),
|
||||
AccessHash: NumberLong("1003"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "green-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0x0a8754,
|
||||
SecondBackgroundColor: 0x2ecc71,
|
||||
ThirdBackgroundColor: null,
|
||||
FourthBackgroundColor: null,
|
||||
Intensity: 50,
|
||||
Rotation: 90,
|
||||
Emoticon: "💚"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "4",
|
||||
Id: NumberLong("4"),
|
||||
AccessHash: NumberLong("1234567890123459"),
|
||||
Slug: "orange",
|
||||
Title: "Orange",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "🧡",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: false,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0xff6b35,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0xff6b35, 0xffa500],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1004"),
|
||||
AccessHash: NumberLong("1004"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "orange-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0xff6b35,
|
||||
SecondBackgroundColor: 0xffa500,
|
||||
ThirdBackgroundColor: null,
|
||||
FourthBackgroundColor: null,
|
||||
Intensity: 50,
|
||||
Rotation: 180,
|
||||
Emoticon: "🧡"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "5",
|
||||
Id: NumberLong("5"),
|
||||
AccessHash: NumberLong("1234567890123460"),
|
||||
Slug: "purple",
|
||||
Title: "Purple",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "💜",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: false,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0x8e44ad,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0x8e44ad, 0xc39bd3],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1005"),
|
||||
AccessHash: NumberLong("1005"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "purple-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0x8e44ad,
|
||||
SecondBackgroundColor: 0xc39bd3,
|
||||
ThirdBackgroundColor: null,
|
||||
FourthBackgroundColor: null,
|
||||
Intensity: 50,
|
||||
Rotation: 225,
|
||||
Emoticon: "💜"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "6",
|
||||
Id: NumberLong("6"),
|
||||
AccessHash: NumberLong("1234567890123461"),
|
||||
Slug: "sunset",
|
||||
Title: "Sunset",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "🌅",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: true,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0xff6b35,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0xff6b35, 0xff8c42, 0xffa94d, 0xffc65c],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1006"),
|
||||
AccessHash: NumberLong("1006"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "sunset-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0xff6b35,
|
||||
SecondBackgroundColor: 0xff8c42,
|
||||
ThirdBackgroundColor: 0xffa94d,
|
||||
FourthBackgroundColor: 0xffc65c,
|
||||
Intensity: 60,
|
||||
Rotation: 315,
|
||||
Emoticon: "🌅"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: "7",
|
||||
Id: NumberLong("7"),
|
||||
AccessHash: NumberLong("1234567890123462"),
|
||||
Slug: "ocean",
|
||||
Title: "Ocean",
|
||||
Default: true,
|
||||
ForChat: true,
|
||||
Emoticon: "🌊",
|
||||
DocumentId: null,
|
||||
Settings: [
|
||||
{
|
||||
MessageColorsAnimated: true,
|
||||
BaseTheme: NumberLong("2"),
|
||||
AccentColor: 0x006994,
|
||||
OutboxAccentColor: null,
|
||||
MessageColors: [0x006994, 0x0099cc, 0x33b5e5, 0x66ccff],
|
||||
WallPaper: {
|
||||
Id: NumberLong("1007"),
|
||||
AccessHash: NumberLong("1007"),
|
||||
Default: true,
|
||||
Pattern: false,
|
||||
Dark: false,
|
||||
Slug: "ocean-gradient",
|
||||
DocumentId: null,
|
||||
Settings: {
|
||||
Blur: false,
|
||||
Motion: true,
|
||||
BackgroundColor: 0x006994,
|
||||
SecondBackgroundColor: 0x0099cc,
|
||||
ThirdBackgroundColor: 0x33b5e5,
|
||||
FourthBackgroundColor: 0x66ccff,
|
||||
Intensity: 60,
|
||||
Rotation: 0,
|
||||
Emoticon: "🌊"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
db.chat_themes.insertMany(themes);
|
||||
|
||||
print(`Добавлено тем оформления чата: ${themes.length}`);
|
||||
}
|
||||
|
||||
// Выводим итоговое количество
|
||||
print(`Total chat themes in database: ${db.chat_themes.countDocuments()}`);
|
||||
@@ -0,0 +1,81 @@
|
||||
# Coturn TURN/STUN Server Configuration for MyTelegram VoIP
|
||||
# Based on RFC 5766 (TURN) and RFC 5389 (STUN)
|
||||
|
||||
# Listen on all interfaces (Docker compatibility)
|
||||
listening-ip=0.0.0.0
|
||||
|
||||
# Listening port for TURN/STUN
|
||||
listening-port=3478
|
||||
|
||||
# TLS listening port (disabled for local testing)
|
||||
# tls-listening-port=5349
|
||||
|
||||
# Relay ports range for media
|
||||
min-port=49152
|
||||
max-port=65535
|
||||
|
||||
# Server realm (domain)
|
||||
realm=mytelegram.local
|
||||
|
||||
# Authentication
|
||||
# Use long-term credentials
|
||||
lt-cred-mech
|
||||
|
||||
# User credentials (username:password)
|
||||
# Format: user=username:password
|
||||
# In production, use strong passwords and rotate regularly
|
||||
user=user:CHANGE_ME
|
||||
|
||||
# Logging
|
||||
verbose
|
||||
log-file=/var/log/coturn/turnserver.log
|
||||
|
||||
# Performance tuning
|
||||
# Total quota (bytes per allocation)
|
||||
total-quota=100
|
||||
# Bandwidth per session (bytes/sec)
|
||||
bps-capacity=0
|
||||
|
||||
# No multicast
|
||||
no-multicast-peers
|
||||
|
||||
# Disable software attributes
|
||||
no-software-attribute
|
||||
|
||||
# Mobility
|
||||
mobility
|
||||
|
||||
# WebRTC compatibility
|
||||
fingerprint
|
||||
|
||||
# Secure communication (TLS disabled for local testing)
|
||||
# no-tls
|
||||
# no-dtls
|
||||
|
||||
# TLS/DTLS certificate paths (optional, for TLS)
|
||||
# cert=/etc/coturn/cert.pem
|
||||
# pkey=/etc/coturn/key.pem
|
||||
|
||||
# CLI
|
||||
cli-password=CHANGE_CLI_PASSWORD
|
||||
|
||||
# Allow TURN for specific IP ranges (optional)
|
||||
# allowed-peer-ip=10.0.0.0-10.255.255.255
|
||||
# denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
|
||||
# Database for persistent storage (optional)
|
||||
# psql-userdb="host=postgresql dbname=coturn user=coturn password=password"
|
||||
|
||||
# Redis for session management (optional)
|
||||
# redis-userdb="ip=redis dbname=0 password=turn connect_timeout=30"
|
||||
|
||||
# Prometheus metrics (optional)
|
||||
# prometheus
|
||||
|
||||
# Disable UDP relay endpoints
|
||||
# no-udp-relay
|
||||
|
||||
# Use external IP (important for NAT)
|
||||
# Must match App__WebRtcConnections__0__Ip in .env
|
||||
# For local testing use 127.0.0.1, for production use your public IP
|
||||
external-ip=127.0.0.1
|
||||
@@ -0,0 +1,29 @@
|
||||
// Update custom emoji documents to add MinIO file path
|
||||
const db = db.getSiblingDB('tg');
|
||||
const collection = db.getCollection('ReadModel-DocumentReadModel');
|
||||
|
||||
// Find all custom emoji documents
|
||||
const customEmojiDocs = collection.find({
|
||||
'Attributes2._t': 'TDocumentAttributeCustomEmoji'
|
||||
});
|
||||
|
||||
let updated = 0;
|
||||
customEmojiDocs.forEach(doc => {
|
||||
const documentId = doc.DocumentId;
|
||||
const subDir = (documentId % 1000).toString().padStart(3, '0');
|
||||
const filePath = `custom_emoji/${subDir}/${documentId}.tgs`;
|
||||
|
||||
// Update document with file path
|
||||
collection.updateOne(
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: {
|
||||
MinIOPath: filePath,
|
||||
MinIOBucket: 'tg-files'
|
||||
}
|
||||
}
|
||||
);
|
||||
updated++;
|
||||
});
|
||||
|
||||
print(`Updated ${updated} custom emoji documents with MinIO paths`);
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
echo Stopping session-server container...
|
||||
docker-compose stop session-server
|
||||
|
||||
echo Removing session-server container...
|
||||
docker-compose rm -f session-server
|
||||
|
||||
echo Removing old session server image (0.36.220.1231)...
|
||||
docker rmi mytelegram/mytelegram-session-server:0.36.220.1231
|
||||
|
||||
echo Pulling session server image v0.32.206.802...
|
||||
docker pull mytelegram/mytelegram-session-server:0.32.206.802
|
||||
|
||||
echo Starting session-server with version 0.32.206.802...
|
||||
set MyTelegramVersion=0.32.206.802
|
||||
docker-compose up -d session-server
|
||||
|
||||
echo Done.
|
||||
pause
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install --production
|
||||
|
||||
COPY server.js ./
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "file-merge-proxy",
|
||||
"version": "1.0.0",
|
||||
"description": "HTTP proxy for merging file parts on file-server",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"json-bigint": "^1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const JSONbig = require('json-bigint')({ storeAsString: true });
|
||||
|
||||
const app = express();
|
||||
|
||||
// Используем json-bigint, чтобы не терять точность больших чисел
|
||||
app.use(express.text({ type: 'application/json' }));
|
||||
app.use((req, res, next) => {
|
||||
if (req.body && typeof req.body === 'string') {
|
||||
try {
|
||||
req.body = JSONbig.parse(req.body);
|
||||
} catch (e) {
|
||||
return res.status(400).json({ error: 'Invalid JSON' });
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
const UPLOAD_BASE = process.env.UPLOAD_PATH || '/app/uploads';
|
||||
|
||||
app.post('/api/files/merge', async (req, res) => {
|
||||
try {
|
||||
const { userId, fileId, parts, fileName } = req.body;
|
||||
|
||||
// Защита от выхода за пределы каталога (path traversal)
|
||||
if (!fileName || typeof fileName !== 'string') {
|
||||
return res.status(400).json({ error: 'Invalid fileName parameter' });
|
||||
}
|
||||
|
||||
// Блокируем попытки обхода пути
|
||||
if (fileName.includes('..') || fileName.includes('/') || fileName.includes('\\')) {
|
||||
console.error(`Заблокирована попытка обхода пути - fileName: ${fileName}, IP: ${req.ip}`);
|
||||
return res.status(400).json({ error: 'Invalid fileName: path traversal not allowed' });
|
||||
}
|
||||
|
||||
// Очищаем имя файла: оставляем только буквы, цифры, дефис, подчёркивание и точки
|
||||
const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, '_');
|
||||
if (sanitizedFileName !== fileName) {
|
||||
console.warn(`Имя файла очищено: "${fileName}" -> "${sanitizedFileName}"`);
|
||||
}
|
||||
|
||||
console.log(`Merging file from local filesystem: userId=${userId}, fileId=${fileId}, parts=${parts}, fileName=${fileName}`);
|
||||
|
||||
// Части файла хранятся в формате: {userId}-{actualFileId}-{partNumber}
|
||||
// fileId из command-server может отличаться от того, что использует file-server,
|
||||
// поэтому реальный fileId находим, перебирая файлы этого пользователя
|
||||
|
||||
console.log(`Searching for files with userId: ${userId}`);
|
||||
|
||||
// Получаем список всех файлов этого пользователя
|
||||
const allFiles = await fs.readdir(UPLOAD_BASE);
|
||||
const userFiles = allFiles.filter(f => f.startsWith(`${userId}-`));
|
||||
|
||||
console.log(`Found ${userFiles.length} files for user ${userId}`);
|
||||
|
||||
if (userFiles.length === 0) {
|
||||
return res.status(404).json({ error: 'No files found for user' });
|
||||
}
|
||||
|
||||
// Группируем файлы по fileId (берём его из имени: userId-fileId-partNumber)
|
||||
const fileGroups = {};
|
||||
for (const file of userFiles) {
|
||||
const match = file.match(/^(\d+)-(-?\d+)-(\d+)$/);
|
||||
if (match) {
|
||||
const [, uid, fid, partNum] = match;
|
||||
if (!fileGroups[fid]) {
|
||||
fileGroups[fid] = [];
|
||||
}
|
||||
fileGroups[fid].push({ file, partNum: parseInt(partNum) });
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Found ${Object.keys(fileGroups).length} file groups:`, Object.keys(fileGroups));
|
||||
|
||||
// Ищем группу с нужным количеством частей
|
||||
let actualFileId = null;
|
||||
let partFiles = null;
|
||||
|
||||
for (const [fid, files] of Object.entries(fileGroups)) {
|
||||
if (files.length === parts) {
|
||||
actualFileId = fid;
|
||||
partFiles = files.sort((a, b) => a.partNum - b.partNum);
|
||||
console.log(`Found matching file group: fileId=${fid}, parts=${files.length}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!actualFileId || !partFiles) {
|
||||
console.error(`No file group with ${parts} parts found`);
|
||||
console.log('Available groups:', Object.entries(fileGroups).map(([fid, files]) => `${fid} (${files.length} parts)`));
|
||||
return res.status(404).json({ error: `No file group with ${parts} parts found` });
|
||||
}
|
||||
|
||||
// Проверяем, что все части на месте
|
||||
const partPaths = [];
|
||||
for (let i = 0; i < parts; i++) {
|
||||
const partFile = partFiles.find(f => f.partNum === i);
|
||||
if (!partFile) {
|
||||
console.error(`Missing part ${i}`);
|
||||
return res.status(404).json({ error: `Missing file part ${i}` });
|
||||
}
|
||||
partPaths.push(path.join(UPLOAD_BASE, partFile.file));
|
||||
}
|
||||
|
||||
console.log(`All ${parts} parts found, merging...`);
|
||||
|
||||
// Склеиваем части
|
||||
const mergedDir = path.join(UPLOAD_BASE, 'merged');
|
||||
await fs.mkdir(mergedDir, { recursive: true });
|
||||
|
||||
// Используем очищенное имя файла
|
||||
const extension = sanitizedFileName.split('.').pop() || '';
|
||||
const mergedFileName = `${userId}-${actualFileId}${extension ? '.' + extension : ''}`;
|
||||
|
||||
// Дополнительно убеждаемся, что итоговый путь остаётся внутри разрешённого каталога
|
||||
const mergedFilePath = path.join(mergedDir, mergedFileName);
|
||||
const resolvedPath = path.resolve(mergedFilePath);
|
||||
const resolvedMergedDir = path.resolve(mergedDir);
|
||||
|
||||
if (!resolvedPath.startsWith(resolvedMergedDir)) {
|
||||
console.error(`Обнаружен выход за пределы каталога. Запрошенный путь: ${resolvedPath}`);
|
||||
return res.status(400).json({ error: 'Invalid file path' });
|
||||
}
|
||||
|
||||
const writeStream = require('fs').createWriteStream(mergedFilePath);
|
||||
let totalSize = 0;
|
||||
|
||||
// Ограничение на размер файла (максимум 2 ГБ)
|
||||
const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024; // 2 ГБ
|
||||
|
||||
for (const partPath of partPaths) {
|
||||
const partData = await fs.readFile(partPath);
|
||||
|
||||
// Проверяем размер перед записью
|
||||
if (totalSize + partData.length > MAX_FILE_SIZE) {
|
||||
console.error(`Превышен лимит размера файла - запрошенный размер: ${totalSize + partData.length}, лимит: ${MAX_FILE_SIZE}`);
|
||||
|
||||
// Удаляем недописанный файл
|
||||
try {
|
||||
writeStream.close();
|
||||
await fs.unlink(mergedFilePath);
|
||||
} catch (e) {}
|
||||
|
||||
return res.status(413).json({ error: 'File too large: Maximum size is 2GB' });
|
||||
}
|
||||
|
||||
writeStream.write(partData);
|
||||
totalSize += partData.length;
|
||||
}
|
||||
|
||||
console.log(`Проверка размера пройдена: ${totalSize} bytes (limit: ${MAX_FILE_SIZE})`);
|
||||
|
||||
writeStream.end();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
writeStream.on('finish', resolve);
|
||||
writeStream.on('error', reject);
|
||||
});
|
||||
|
||||
console.log(`Merged ${parts} parts into ${mergedFileName}, total size: ${totalSize} bytes`);
|
||||
|
||||
// Удаляем части после склейки
|
||||
for (const partPath of partPaths) {
|
||||
try {
|
||||
await fs.unlink(partPath);
|
||||
} catch (e) {
|
||||
console.error(`Failed to remove part ${partPath}:`, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
filePath: path.join('merged', mergedFileName),
|
||||
totalSize: totalSize
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Merge error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 5000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`File merge proxy listening on port ${PORT}`);
|
||||
console.log(`Upload base path: ${UPLOAD_BASE}`);
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
# Mediasoup SFU Configuration
|
||||
PORT=3200
|
||||
MEDIASOUP_LISTEN_IP=0.0.0.0
|
||||
MEDIASOUP_ANNOUNCED_IP=127.0.0.1
|
||||
RTC_MIN_PORT=10000
|
||||
RTC_MAX_PORT=10100
|
||||
LOG_LEVEL=debug
|
||||
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
*.log
|
||||
.env.local
|
||||
.DS_Store
|
||||
@@ -0,0 +1,27 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
# Install dependencies for Mediasoup
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
linux-headers
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install npm dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 3200
|
||||
EXPOSE 10000-10100/udp
|
||||
|
||||
# Start server
|
||||
CMD ["node", "server.js"]
|
||||
Generated
+2013
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "mytelegram-mediasoup-sfu",
|
||||
"version": "1.0.0",
|
||||
"description": "Mediasoup SFU server for MyTelegram group calls",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js"
|
||||
},
|
||||
"keywords": ["mediasoup", "webrtc", "sfu", "telegram"],
|
||||
"author": "MyTelegram",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mediasoup": "^3.13.0",
|
||||
"express": "^4.18.2",
|
||||
"socket.io": "^4.6.1",
|
||||
"cors": "^2.8.5",
|
||||
"winston": "^3.11.0",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,703 @@
|
||||
/**
|
||||
* MyTelegram Mediasoup SFU Server
|
||||
* Production-ready WebRTC SFU for group calls
|
||||
*/
|
||||
|
||||
const mediasoup = require('mediasoup');
|
||||
const express = require('express');
|
||||
const http = require('http');
|
||||
const socketIO = require('socket.io');
|
||||
const cors = require('cors');
|
||||
const winston = require('winston');
|
||||
require('dotenv').config();
|
||||
|
||||
// ==================== Configuration ====================
|
||||
|
||||
const config = {
|
||||
listenIp: process.env.MEDIASOUP_LISTEN_IP || '0.0.0.0',
|
||||
announcedIp: process.env.MEDIASOUP_ANNOUNCED_IP || '127.0.0.1',
|
||||
httpPort: parseInt(process.env.PORT) || 3200,
|
||||
rtcMinPort: parseInt(process.env.RTC_MIN_PORT) || 10000,
|
||||
rtcMaxPort: parseInt(process.env.RTC_MAX_PORT) || 10100,
|
||||
logLevel: process.env.LOG_LEVEL || 'debug',
|
||||
};
|
||||
|
||||
// ==================== Logger Setup ====================
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: config.logLevel,
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
||||
return `[${timestamp}] [${level.toUpperCase()}] ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.File({ filename: 'mediasoup-error.log', level: 'error' }),
|
||||
new winston.transports.File({ filename: 'mediasoup-combined.log' })
|
||||
]
|
||||
});
|
||||
|
||||
// ==================== Express App Setup ====================
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
const server = http.createServer(app);
|
||||
const io = socketIO(server, {
|
||||
cors: {
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST']
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== Global State ====================
|
||||
|
||||
let worker;
|
||||
let router;
|
||||
const rooms = new Map(); // roomId -> Room
|
||||
const peers = new Map(); // socketId -> Peer
|
||||
|
||||
class Room {
|
||||
constructor(roomId) {
|
||||
this.id = roomId;
|
||||
this.peers = new Map(); // peerId -> Peer
|
||||
this.router = null;
|
||||
logger.info(`Room created: ${roomId}`);
|
||||
}
|
||||
|
||||
addPeer(peer) {
|
||||
this.peers.set(peer.id, peer);
|
||||
logger.info(`Peer ${peer.id} added to room ${this.id}`);
|
||||
}
|
||||
|
||||
removePeer(peerId) {
|
||||
const peer = this.peers.get(peerId);
|
||||
if (peer) {
|
||||
peer.close();
|
||||
this.peers.delete(peerId);
|
||||
logger.info(`Peer ${peerId} removed from room ${this.id}`);
|
||||
}
|
||||
|
||||
if (this.peers.size === 0) {
|
||||
logger.info(`Room ${this.id} is empty, closing...`);
|
||||
return true; // Room can be deleted
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getPeer(peerId) {
|
||||
return this.peers.get(peerId);
|
||||
}
|
||||
|
||||
getAllPeers() {
|
||||
return Array.from(this.peers.values());
|
||||
}
|
||||
|
||||
close() {
|
||||
for (const peer of this.peers.values()) {
|
||||
peer.close();
|
||||
}
|
||||
this.peers.clear();
|
||||
logger.info(`Room ${this.id} closed`);
|
||||
}
|
||||
}
|
||||
|
||||
class Peer {
|
||||
constructor(id, socket, roomId) {
|
||||
this.id = id;
|
||||
this.socket = socket;
|
||||
this.roomId = roomId;
|
||||
this.transports = new Map(); // transportId -> Transport
|
||||
this.producers = new Map(); // producerId -> Producer
|
||||
this.consumers = new Map(); // consumerId -> Consumer
|
||||
this.rtpCapabilities = null;
|
||||
logger.info(`Peer created: ${id} in room ${roomId}`);
|
||||
}
|
||||
|
||||
addTransport(transport) {
|
||||
this.transports.set(transport.id, transport);
|
||||
logger.debug(`Transport ${transport.id} added to peer ${this.id}`);
|
||||
}
|
||||
|
||||
addProducer(producer) {
|
||||
this.producers.set(producer.id, producer);
|
||||
logger.info(`Producer ${producer.id} (${producer.kind}) added to peer ${this.id}`);
|
||||
}
|
||||
|
||||
addConsumer(consumer) {
|
||||
this.consumers.set(consumer.id, consumer);
|
||||
logger.info(`Consumer ${consumer.id} (${consumer.kind}) added to peer ${this.id}`);
|
||||
}
|
||||
|
||||
close() {
|
||||
logger.info(`Closing peer ${this.id}`);
|
||||
|
||||
// Close all consumers
|
||||
for (const consumer of this.consumers.values()) {
|
||||
consumer.close();
|
||||
}
|
||||
this.consumers.clear();
|
||||
|
||||
// Close all producers
|
||||
for (const producer of this.producers.values()) {
|
||||
producer.close();
|
||||
}
|
||||
this.producers.clear();
|
||||
|
||||
// Close all transports
|
||||
for (const transport of this.transports.values()) {
|
||||
transport.close();
|
||||
}
|
||||
this.transports.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Mediasoup Worker & Router ====================
|
||||
|
||||
async function createWorker() {
|
||||
logger.info('Creating Mediasoup worker...');
|
||||
|
||||
worker = await mediasoup.createWorker({
|
||||
logLevel: 'warn',
|
||||
rtcMinPort: config.rtcMinPort,
|
||||
rtcMaxPort: config.rtcMaxPort,
|
||||
});
|
||||
|
||||
worker.on('died', () => {
|
||||
logger.error('Mediasoup worker died, exiting in 2 seconds...');
|
||||
setTimeout(() => process.exit(1), 2000);
|
||||
});
|
||||
|
||||
logger.info(`Mediasoup worker created [PID: ${worker.pid}]`);
|
||||
return worker;
|
||||
}
|
||||
|
||||
async function createRouter() {
|
||||
logger.info('Creating Mediasoup router...');
|
||||
|
||||
const mediaCodecs = [
|
||||
{
|
||||
kind: 'audio',
|
||||
mimeType: 'audio/opus',
|
||||
clockRate: 48000,
|
||||
channels: 2
|
||||
},
|
||||
{
|
||||
kind: 'video',
|
||||
mimeType: 'video/VP8',
|
||||
clockRate: 90000,
|
||||
parameters: {
|
||||
'x-google-start-bitrate': 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'video',
|
||||
mimeType: 'video/H264',
|
||||
clockRate: 90000,
|
||||
parameters: {
|
||||
'packetization-mode': 1,
|
||||
'profile-level-id': '42e01f',
|
||||
'level-asymmetry-allowed': 1
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
router = await worker.createRouter({ mediaCodecs });
|
||||
logger.info(`Mediasoup router created [ID: ${router.id}]`);
|
||||
return router;
|
||||
}
|
||||
|
||||
// ==================== HTTP API Endpoints ====================
|
||||
|
||||
// Health check
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
worker: {
|
||||
pid: worker?.pid,
|
||||
dead: worker?.closed
|
||||
},
|
||||
router: {
|
||||
id: router?.id,
|
||||
closed: router?.closed
|
||||
},
|
||||
rooms: rooms.size,
|
||||
peers: peers.size
|
||||
});
|
||||
});
|
||||
|
||||
// Get DTLS fingerprint (for MyTelegram backend)
|
||||
app.get('/api/dtls-fingerprint', (req, res) => {
|
||||
if (!router) {
|
||||
return res.status(503).json({ error: 'Router not initialized' });
|
||||
}
|
||||
|
||||
const fingerprint = router.rtpCapabilities.headerExtensions.length > 0
|
||||
? router.rtpCapabilities
|
||||
: null;
|
||||
|
||||
// Get DTLS parameters from a temporary transport
|
||||
router.createWebRtcTransport({
|
||||
listenIps: [{ ip: config.listenIp, announcedIp: config.announcedIp }],
|
||||
enableUdp: true,
|
||||
enableTcp: false,
|
||||
preferUdp: true
|
||||
}).then(transport => {
|
||||
const dtls = transport.dtlsParameters;
|
||||
res.json({
|
||||
algorithm: dtls.fingerprints[0].algorithm,
|
||||
value: dtls.fingerprints[0].value
|
||||
});
|
||||
transport.close();
|
||||
}).catch(err => {
|
||||
logger.error('Failed to get DTLS fingerprint:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
});
|
||||
});
|
||||
|
||||
// Get router RTP capabilities
|
||||
app.get('/api/rtp-capabilities', (req, res) => {
|
||||
if (!router) {
|
||||
return res.status(503).json({ error: 'Router not initialized' });
|
||||
}
|
||||
res.json(router.rtpCapabilities);
|
||||
});
|
||||
|
||||
// Get room info
|
||||
app.get('/api/rooms/:roomId', (req, res) => {
|
||||
const room = rooms.get(req.params.roomId);
|
||||
if (!room) {
|
||||
return res.status(404).json({ error: 'Room not found' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
id: room.id,
|
||||
peers: room.getAllPeers().map(p => ({
|
||||
id: p.id,
|
||||
producers: p.producers.size,
|
||||
consumers: p.consumers.size
|
||||
}))
|
||||
});
|
||||
});
|
||||
|
||||
// Create WebRTC transport for Telegram MTProto integration
|
||||
app.post('/api/transports/create', async (req, res) => {
|
||||
try {
|
||||
const { roomId, peerId, direction } = req.body;
|
||||
|
||||
logger.info(`Creating ${direction} transport for peer ${peerId} in room ${roomId}`);
|
||||
|
||||
const transport = await router.createWebRtcTransport({
|
||||
listenIps: [
|
||||
{
|
||||
ip: config.listenIp,
|
||||
announcedIp: config.announcedIp
|
||||
}
|
||||
],
|
||||
enableUdp: true,
|
||||
enableTcp: false,
|
||||
preferUdp: true,
|
||||
initialAvailableOutgoingBitrate: 1000000,
|
||||
minimumAvailableOutgoingBitrate: 600000,
|
||||
maxSctpMessageSize: 262144,
|
||||
maxIncomingBitrate: 1500000
|
||||
});
|
||||
|
||||
// Store transport info
|
||||
const key = `${roomId}:${peerId}:${direction}`;
|
||||
if (!global.transports) global.transports = new Map();
|
||||
global.transports.set(key, transport);
|
||||
global.transports.set(transport.id, transport);
|
||||
|
||||
logger.info(`Transport ${transport.id} created on port ${transport.tuple?.localPort || 'dynamic'}`);
|
||||
|
||||
res.json({
|
||||
id: transport.id,
|
||||
iceParameters: transport.iceParameters,
|
||||
iceCandidates: transport.iceCandidates,
|
||||
dtlsParameters: transport.dtlsParameters
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error creating transport:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Connect transport with DTLS parameters from client
|
||||
app.post('/api/transports/connect', async (req, res) => {
|
||||
try {
|
||||
const { transportId, dtlsParameters } = req.body;
|
||||
|
||||
logger.info(`Connecting transport ${transportId}`);
|
||||
|
||||
const transport = global.transports?.get(transportId);
|
||||
if (!transport) {
|
||||
return res.status(404).json({ error: 'Transport not found' });
|
||||
}
|
||||
|
||||
await transport.connect({ dtlsParameters });
|
||||
|
||||
logger.info(`Transport ${transportId} connected`);
|
||||
res.json({ success: true });
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error connecting transport:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Create producer (for sending media)
|
||||
app.post('/api/produce', async (req, res) => {
|
||||
try {
|
||||
const { transportId, kind, rtpParameters } = req.body;
|
||||
|
||||
logger.info(`Creating producer on transport ${transportId}, kind: ${kind}`);
|
||||
|
||||
const transport = global.transports?.get(transportId);
|
||||
if (!transport) {
|
||||
return res.status(404).json({ error: 'Transport not found' });
|
||||
}
|
||||
|
||||
const producer = await transport.produce({
|
||||
kind,
|
||||
rtpParameters
|
||||
});
|
||||
|
||||
logger.info(`Producer ${producer.id} created`);
|
||||
res.json({ producerId: producer.id });
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error creating producer:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Create consumer (for receiving media)
|
||||
app.post('/api/consume', async (req, res) => {
|
||||
try {
|
||||
const { transportId, producerId, rtpCapabilities } = req.body;
|
||||
|
||||
logger.info(`Creating consumer on transport ${transportId} for producer ${producerId}`);
|
||||
|
||||
const transport = global.transports?.get(transportId);
|
||||
if (!transport) {
|
||||
return res.status(404).json({ error: 'Transport not found' });
|
||||
}
|
||||
|
||||
// Check if can consume
|
||||
if (!router.canConsume({ producerId, rtpCapabilities })) {
|
||||
return res.status(400).json({ error: 'Cannot consume' });
|
||||
}
|
||||
|
||||
const consumer = await transport.consume({
|
||||
producerId,
|
||||
rtpCapabilities,
|
||||
paused: false
|
||||
});
|
||||
|
||||
logger.info(`Consumer ${consumer.id} created`);
|
||||
|
||||
res.json({
|
||||
id: consumer.id,
|
||||
producerId: producerId,
|
||||
kind: consumer.kind,
|
||||
rtpParameters: consumer.rtpParameters
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error creating consumer:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== Socket.IO Signaling ====================
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
logger.info(`Socket connected: ${socket.id}`);
|
||||
|
||||
socket.on('join-room', async ({ roomId, peerId }, callback) => {
|
||||
try {
|
||||
logger.info(`Peer ${peerId} joining room ${roomId}`);
|
||||
|
||||
// Get or create room
|
||||
let room = rooms.get(roomId);
|
||||
if (!room) {
|
||||
room = new Room(roomId);
|
||||
room.router = router;
|
||||
rooms.set(roomId, room);
|
||||
}
|
||||
|
||||
// Create peer
|
||||
const peer = new Peer(peerId, socket, roomId);
|
||||
room.addPeer(peer);
|
||||
peers.set(socket.id, peer);
|
||||
|
||||
socket.join(roomId);
|
||||
|
||||
callback({
|
||||
success: true,
|
||||
rtpCapabilities: router.rtpCapabilities
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error joining room:', error);
|
||||
callback({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('create-transport', async ({ roomId, direction }, callback) => {
|
||||
try {
|
||||
const peer = peers.get(socket.id);
|
||||
if (!peer) {
|
||||
throw new Error('Peer not found');
|
||||
}
|
||||
|
||||
const transport = await router.createWebRtcTransport({
|
||||
listenIps: [
|
||||
{
|
||||
ip: config.listenIp,
|
||||
announcedIp: config.announcedIp
|
||||
}
|
||||
],
|
||||
enableUdp: true,
|
||||
enableTcp: false,
|
||||
preferUdp: true,
|
||||
initialAvailableOutgoingBitrate: 1000000,
|
||||
minimumAvailableOutgoingBitrate: 600000,
|
||||
maxSctpMessageSize: 262144,
|
||||
maxIncomingBitrate: 1500000
|
||||
});
|
||||
|
||||
peer.addTransport(transport);
|
||||
|
||||
transport.on('dtlsstatechange', (dtlsState) => {
|
||||
logger.debug(`Transport ${transport.id} DTLS state: ${dtlsState}`);
|
||||
if (dtlsState === 'closed') {
|
||||
transport.close();
|
||||
}
|
||||
});
|
||||
|
||||
transport.on('close', () => {
|
||||
logger.info(`Transport ${transport.id} closed`);
|
||||
});
|
||||
|
||||
callback({
|
||||
success: true,
|
||||
transport: {
|
||||
id: transport.id,
|
||||
iceParameters: transport.iceParameters,
|
||||
iceCandidates: transport.iceCandidates,
|
||||
dtlsParameters: transport.dtlsParameters
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error creating transport:', error);
|
||||
callback({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('connect-transport', async ({ transportId, dtlsParameters }, callback) => {
|
||||
try {
|
||||
const peer = peers.get(socket.id);
|
||||
if (!peer) {
|
||||
throw new Error('Peer not found');
|
||||
}
|
||||
|
||||
const transport = peer.transports.get(transportId);
|
||||
if (!transport) {
|
||||
throw new Error('Transport not found');
|
||||
}
|
||||
|
||||
await transport.connect({ dtlsParameters });
|
||||
logger.info(`Transport ${transportId} connected for peer ${peer.id}`);
|
||||
|
||||
callback({ success: true });
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error connecting transport:', error);
|
||||
callback({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('produce', async ({ transportId, kind, rtpParameters }, callback) => {
|
||||
try {
|
||||
const peer = peers.get(socket.id);
|
||||
if (!peer) {
|
||||
throw new Error('Peer not found');
|
||||
}
|
||||
|
||||
const transport = peer.transports.get(transportId);
|
||||
if (!transport) {
|
||||
throw new Error('Transport not found');
|
||||
}
|
||||
|
||||
const producer = await transport.produce({
|
||||
kind,
|
||||
rtpParameters
|
||||
});
|
||||
|
||||
peer.addProducer(producer);
|
||||
|
||||
producer.on('transportclose', () => {
|
||||
logger.info(`Producer ${producer.id} transport closed`);
|
||||
producer.close();
|
||||
peer.producers.delete(producer.id);
|
||||
});
|
||||
|
||||
// Notify other peers in room about new producer
|
||||
const room = rooms.get(peer.roomId);
|
||||
if (room) {
|
||||
socket.to(peer.roomId).emit('new-producer', {
|
||||
peerId: peer.id,
|
||||
producerId: producer.id,
|
||||
kind: producer.kind
|
||||
});
|
||||
}
|
||||
|
||||
callback({
|
||||
success: true,
|
||||
producerId: producer.id
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error producing:', error);
|
||||
callback({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('consume', async ({ transportId, producerId, rtpCapabilities }, callback) => {
|
||||
try {
|
||||
const peer = peers.get(socket.id);
|
||||
if (!peer) {
|
||||
throw new Error('Peer not found');
|
||||
}
|
||||
|
||||
const transport = peer.transports.get(transportId);
|
||||
if (!transport) {
|
||||
throw new Error('Transport not found');
|
||||
}
|
||||
|
||||
// Find the producer peer
|
||||
const room = rooms.get(peer.roomId);
|
||||
let producer = null;
|
||||
for (const otherPeer of room.getAllPeers()) {
|
||||
producer = otherPeer.producers.get(producerId);
|
||||
if (producer) break;
|
||||
}
|
||||
|
||||
if (!producer) {
|
||||
throw new Error('Producer not found');
|
||||
}
|
||||
|
||||
// Check if router can consume
|
||||
if (!router.canConsume({ producerId, rtpCapabilities })) {
|
||||
throw new Error('Cannot consume');
|
||||
}
|
||||
|
||||
const consumer = await transport.consume({
|
||||
producerId,
|
||||
rtpCapabilities,
|
||||
paused: false
|
||||
});
|
||||
|
||||
peer.addConsumer(consumer);
|
||||
|
||||
consumer.on('transportclose', () => {
|
||||
logger.info(`Consumer ${consumer.id} transport closed`);
|
||||
consumer.close();
|
||||
peer.consumers.delete(consumer.id);
|
||||
});
|
||||
|
||||
consumer.on('producerclose', () => {
|
||||
logger.info(`Consumer ${consumer.id} producer closed`);
|
||||
socket.emit('consumer-closed', { consumerId: consumer.id });
|
||||
consumer.close();
|
||||
peer.consumers.delete(consumer.id);
|
||||
});
|
||||
|
||||
callback({
|
||||
success: true,
|
||||
consumer: {
|
||||
id: consumer.id,
|
||||
producerId: producerId,
|
||||
kind: consumer.kind,
|
||||
rtpParameters: consumer.rtpParameters
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error consuming:', error);
|
||||
callback({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
logger.info(`Socket disconnected: ${socket.id}`);
|
||||
|
||||
const peer = peers.get(socket.id);
|
||||
if (peer) {
|
||||
const room = rooms.get(peer.roomId);
|
||||
if (room) {
|
||||
const isEmpty = room.removePeer(peer.id);
|
||||
if (isEmpty) {
|
||||
rooms.delete(room.id);
|
||||
}
|
||||
|
||||
// Notify other peers
|
||||
socket.to(peer.roomId).emit('peer-left', { peerId: peer.id });
|
||||
}
|
||||
peers.delete(socket.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ==================== Server Startup ====================
|
||||
|
||||
async function startServer() {
|
||||
try {
|
||||
await createWorker();
|
||||
await createRouter();
|
||||
|
||||
server.listen(config.httpPort, () => {
|
||||
logger.info(`==============================================`);
|
||||
logger.info(`MyTelegram Mediasoup SFU Server Started`);
|
||||
logger.info(`==============================================`);
|
||||
logger.info(`HTTP API: http://localhost:${config.httpPort}`);
|
||||
logger.info(`WebRTC RTC Ports: ${config.rtcMinPort}-${config.rtcMaxPort}`);
|
||||
logger.info(`Announced IP: ${config.announcedIp}`);
|
||||
logger.info(`==============================================`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to start server:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', async () => {
|
||||
logger.info('Shutting down gracefully...');
|
||||
|
||||
// Close all rooms
|
||||
for (const room of rooms.values()) {
|
||||
room.close();
|
||||
}
|
||||
rooms.clear();
|
||||
peers.clear();
|
||||
|
||||
if (worker) {
|
||||
worker.close();
|
||||
}
|
||||
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
startServer();
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
find . -iname "Logs" | xargs rm -rf
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
find . -iname "bin" | xargs rm -rf
|
||||
find . -iname "obj" | xargs rm -rf
|
||||
@@ -0,0 +1,240 @@
|
||||
:: For more information,please visit https://github.com/loyldg/mytelegram
|
||||
set App__QueryServerEventStoreDatabaseName=tg-1
|
||||
set App__QueryServerReadModelDatabaseName=tg-1
|
||||
set App__DatabaseName=tg
|
||||
set App__BotDatabaseName=tg
|
||||
|
||||
set App__FixedVerifyCode=22222
|
||||
|
||||
set Serilog__MinimumLevel__Default=Information
|
||||
set Serilog__MinimumLevel__Override__Microsoft=Information
|
||||
|
||||
:: Use separate media server
|
||||
set App__Servers__2__Enabled=True
|
||||
set App__Servers__3__Enabled=True
|
||||
|
||||
:: RabbitMQ
|
||||
:: set RabbitMQ__Connections__Default__HostName=localhost
|
||||
:: set RabbitMQ__Connections__Default__Password=test
|
||||
:: set RabbitMQ__Connections__Default__Port=5672
|
||||
:: set RabbitMQ__Connections__Default__UserName=test
|
||||
:: set RabbitMQ__EventBus__ExchangeName=MyTelegramExchange
|
||||
|
||||
:: Redis
|
||||
:: set Redis__Configuration=localhost:6379
|
||||
|
||||
:: MongoDB
|
||||
:: set ConnectionStrings__Default=mongodb://localhost:27017
|
||||
|
||||
:: Minio
|
||||
:: set Minio__AccessKey=test
|
||||
:: set Minio__BucketName=tg-files
|
||||
:: set Minio__CreateBucketIfNotExists=True
|
||||
:: set Minio__Endpoint=localhost:9000
|
||||
:: set Minio__SecretKey=12345678
|
||||
|
||||
:: TwilioSms
|
||||
:: set TwilioSms__AccountSId=
|
||||
:: set TwilioSms__AuthToken=
|
||||
:: set TwilioSms__Enabled=False
|
||||
:: set TwilioSms__FromNumber=
|
||||
:: VonageSms
|
||||
:: set VonageSms__ApiKey=
|
||||
:: set VonageSms__ApiSecret=
|
||||
:: set VonageSms__BrandName=
|
||||
:: set VonageSms__Enabled=False
|
||||
|
||||
:: App
|
||||
:: set App=
|
||||
:: set App__AllowedUserIds__0=2000001
|
||||
:: set App__AutoCreateSuperGroup=True
|
||||
:: set App__BotDatabaseName=tg
|
||||
:: set App__ChannelGetDifferenceIntervalSeconds=60
|
||||
:: set App__CommandServerObjectIds=
|
||||
:: set App__DatabaseName=tg
|
||||
:: set App__DcOptions__0__Cdn=False
|
||||
:: set App__DcOptions__0__Enabled=True
|
||||
:: set App__DcOptions__0__Id=1
|
||||
:: set App__DcOptions__0__IpAddress=192.168.1.100
|
||||
:: set App__DcOptions__0__Ipv6=False
|
||||
:: set App__DcOptions__0__MediaOnly=False
|
||||
:: set App__DcOptions__0__Port=20443
|
||||
:: set App__DcOptions__0__Static=False
|
||||
:: set App__DcOptions__0__TcpoOnly=True
|
||||
:: set App__DcOptions__0__ThisPortOnly=True
|
||||
:: set App__DcOptions__1__Cdn=False
|
||||
:: set App__DcOptions__1__Enabled=True
|
||||
:: set App__DcOptions__1__Id=2
|
||||
:: set App__DcOptions__1__IpAddress=192.168.1.100
|
||||
:: set App__DcOptions__1__Ipv6=False
|
||||
:: set App__DcOptions__1__MediaOnly=False
|
||||
:: set App__DcOptions__1__Port=20543
|
||||
:: set App__DcOptions__1__Static=False
|
||||
:: set App__DcOptions__1__TcpoOnly=True
|
||||
:: set App__DcOptions__1__ThisPortOnly=True
|
||||
:: set App__DcOptions__2__Cdn=False
|
||||
:: set App__DcOptions__2__Enabled=True
|
||||
:: set App__DcOptions__2__Id=2
|
||||
:: set App__DcOptions__2__IpAddress=192.168.1.100
|
||||
:: set App__DcOptions__2__Ipv6=False
|
||||
:: set App__DcOptions__2__MediaOnly=True
|
||||
:: set App__DcOptions__2__Port=20643
|
||||
:: set App__DcOptions__2__Static=False
|
||||
:: set App__DcOptions__2__TcpoOnly=True
|
||||
:: set App__DcOptions__2__ThisPortOnly=True
|
||||
:: set App__DcOptions__3__Cdn=False
|
||||
:: set App__DcOptions__3__Enabled=True
|
||||
:: set App__DcOptions__3__Id=2
|
||||
:: set App__DcOptions__3__IpAddress=192.168.1.100
|
||||
:: set App__DcOptions__3__Ipv6=False
|
||||
:: set App__DcOptions__3__MediaOnly=True
|
||||
:: set App__DcOptions__3__Port=20644
|
||||
:: set App__DcOptions__3__Static=False
|
||||
:: set App__DcOptions__3__TcpoOnly=True
|
||||
:: set App__DcOptions__3__ThisPortOnly=True
|
||||
:: set App__DispatchUnknownObjectToMessengerServer=True
|
||||
:: set App__EnableFutureAuthToken=True
|
||||
:: set App__FileServerGrpcServiceUrl=http://localhost:10001
|
||||
:: set App__FixedVerifyCode=
|
||||
:: set App__IdGeneratorGrpcServiceUrl=http://localhost:10002
|
||||
:: set App__IsMediaDc=False
|
||||
:: set App__JoinChatDomain=https://t.me
|
||||
:: set App__LoadAllStickersIntoMemory=True
|
||||
:: set App__MediaDcId=2
|
||||
:: set App__MediaOnly=False
|
||||
:: set App__MessengerServerGrpcServiceUrl=http://localhost:10003
|
||||
:: set App__OfficialTelegramBotOptions__ProxyUrl=socks5://localhost:10808
|
||||
:: set App__OfficialTelegramBotOptions__Token=
|
||||
:: set App__OfficialTelegramBotOptions__UseProxy=True
|
||||
:: set App__PrivateKeyFilePath=private.pkcs8.key
|
||||
:: set App__QueryServerEventStoreDatabaseName=tg-1
|
||||
:: set App__QueryServerReadModelDatabaseName=tg-1
|
||||
:: set App__SendWelcomeMessageAfterUserSignIn=False
|
||||
:: set App__Servers__0__CertPemFilePath=
|
||||
:: set App__Servers__0__Enabled=True
|
||||
:: set App__Servers__0__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__0__Ip=
|
||||
:: set App__Servers__0__Ipv6=True
|
||||
:: set App__Servers__0__KeyPemFilePath=
|
||||
:: set App__Servers__0__MediaOnly=False
|
||||
:: set App__Servers__0__Port=20443
|
||||
:: set App__Servers__0__ServerType=0
|
||||
:: set App__Servers__0__Ssl=False
|
||||
:: set App__Servers__1__CertPemFilePath=
|
||||
:: set App__Servers__1__Enabled=True
|
||||
:: set App__Servers__1__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__1__Ip=
|
||||
:: set App__Servers__1__Ipv6=True
|
||||
:: set App__Servers__1__KeyPemFilePath=
|
||||
:: set App__Servers__1__MediaOnly=False
|
||||
:: set App__Servers__1__Port=20543
|
||||
:: set App__Servers__1__ServerType=0
|
||||
:: set App__Servers__1__Ssl=False
|
||||
:: set App__Servers__2__CertPemFilePath=
|
||||
:: set App__Servers__2__Enabled=False
|
||||
:: set App__Servers__2__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__2__Ip=
|
||||
:: set App__Servers__2__Ipv6=True
|
||||
:: set App__Servers__2__KeyPemFilePath=
|
||||
:: set App__Servers__2__MediaOnly=True
|
||||
:: set App__Servers__2__Port=20643
|
||||
:: set App__Servers__2__ServerType=0
|
||||
:: set App__Servers__2__Ssl=False
|
||||
:: set App__Servers__3__CertPemFilePath=
|
||||
:: set App__Servers__3__Enabled=False
|
||||
:: set App__Servers__3__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__3__Ip=
|
||||
:: set App__Servers__3__Ipv6=True
|
||||
:: set App__Servers__3__KeyPemFilePath=
|
||||
:: set App__Servers__3__MediaOnly=True
|
||||
:: set App__Servers__3__Port=20644
|
||||
:: set App__Servers__3__ServerType=0
|
||||
:: set App__Servers__3__Ssl=False
|
||||
:: set App__Servers__4__CertPemFilePath=_wildcard.telegram2.com.pem
|
||||
:: set App__Servers__4__Enabled=True
|
||||
:: set App__Servers__4__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__4__Ip=
|
||||
:: set App__Servers__4__Ipv6=True
|
||||
:: set App__Servers__4__KeyPemFilePath=_wildcard.telegram2.com-key.pem
|
||||
:: set App__Servers__4__MediaOnly=False
|
||||
:: set App__Servers__4__Port=30443
|
||||
:: set App__Servers__4__ServerType=1
|
||||
:: set App__Servers__4__Ssl=True
|
||||
:: set App__Servers__5__CertPemFilePath=
|
||||
:: set App__Servers__5__Enabled=True
|
||||
:: set App__Servers__5__EnableProxyProtocolV2=False
|
||||
:: set App__Servers__5__Ip=
|
||||
:: set App__Servers__5__Ipv6=True
|
||||
:: set App__Servers__5__KeyPemFilePath=
|
||||
:: set App__Servers__5__MediaOnly=False
|
||||
:: set App__Servers__5__Port=30444
|
||||
:: set App__Servers__5__ServerType=1
|
||||
:: set App__Servers__5__Ssl=False
|
||||
:: set App__SetPremiumToTrueAfterUserCreated=True
|
||||
:: set App__SubscribeLocalRequest=True
|
||||
:: set App__SubscribeRemoteRequest=True
|
||||
:: set App__TempAuthKeyExpirationMinutes=720
|
||||
:: set App__ThisDcId=1
|
||||
:: set App__UploadRootPath=uploads2
|
||||
:: set App__UseExternalWebHookSender=False
|
||||
:: set App__UseInMemoryFilters=True
|
||||
:: set App__VerificationCodeExpirationSeconds=300
|
||||
:: set App__VerificationCodeLength=5
|
||||
:: set App__WebHookSenderUrl=
|
||||
:: set App__WebRtcConnections__0__Ip=192.168.1.100
|
||||
:: set App__WebRtcConnections__0__Ipv6=
|
||||
:: set App__WebRtcConnections__0__Password=b
|
||||
:: set App__WebRtcConnections__0__Port=20444
|
||||
:: set App__WebRtcConnections__0__Stun=True
|
||||
:: set App__WebRtcConnections__0__Turn=True
|
||||
:: set App__WebRtcConnections__0__UserName=a
|
||||
|
||||
|
||||
|
||||
cd ./command-server
|
||||
start MyTelegram.Messenger.CommandServer.exe
|
||||
|
||||
cd ../query-server
|
||||
start MyTelegram.Messenger.QueryServer.exe
|
||||
|
||||
cd ../gateway
|
||||
start MyTelegram.GatewayServer.exe
|
||||
|
||||
:: start MyTelegram.GatewayServer.exe ^
|
||||
:: RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:ClientName=MyTelegramGatewayServer.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly ^
|
||||
:: App:Servers:0:Enabled=false ^
|
||||
:: App:Servers:1:Enabled=false ^
|
||||
:: App:Servers:2:Enabled=true ^
|
||||
:: App:Servers:3:Enabled=true ^
|
||||
:: App:Servers:4:Enabled=false ^
|
||||
:: App:Servers:5:Enabled=false
|
||||
|
||||
cd ../auth
|
||||
start MyTelegram.AuthServer.exe
|
||||
|
||||
:: start MyTelegram.AuthServer.exe ^
|
||||
:: RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:ClientName=MyTelegramAuthServer.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly
|
||||
|
||||
cd ../session
|
||||
start MyTelegram.SessionServer.exe
|
||||
:: start MyTelegram.SessionServer.exe ^
|
||||
:: RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly ^
|
||||
:: App:MediaOnly=true ^
|
||||
:: RabbitMQ:EventBus:ClientName=MyTelegramSessionServer.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly
|
||||
|
||||
cd ../file
|
||||
start MyTelegram.FileServer.exe urls=http://.+:10001
|
||||
:: start MyTelegram.FileServer.exe urls=http://.+:10007 ^
|
||||
:: RabbitMQ:EventBus:ClientName=MyTelegramFileServer.MediaOnly ^
|
||||
:: RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly
|
||||
|
||||
cd ../messenger-grpc-service
|
||||
start MyTelegram.MessengerServer.GrpcService.exe urls=http://.+:10003
|
||||
|
||||
cd ../sms
|
||||
start MyTelegram.SmsSender.exe
|
||||
@@ -0,0 +1,253 @@
|
||||
# For more information,please visit https://github.com/loyldg/mytelegram
|
||||
$env:App__QueryServerEventStoreDatabaseName = "tg-1"
|
||||
$env:App__QueryServerReadModelDatabaseName = "tg-1"
|
||||
$env:App__DatabaseName = "tg"
|
||||
$env:App__BotDatabaseName = "tg"
|
||||
|
||||
$env:App__FixedVerifyCode = "22222"
|
||||
|
||||
$env:Serilog__MinimumLevel__Default = "Information"
|
||||
$env:Serilog__MinimumLevel__Override__Microsoft = "Information"
|
||||
|
||||
# Use separate media server
|
||||
$env:App__Servers__2__Enabled = "True"
|
||||
$env:App__Servers__3__Enabled = "True"
|
||||
|
||||
|
||||
# RabbitMQ
|
||||
# $env:RabbitMQ__Connections__Default__HostName=localhost
|
||||
# $env:RabbitMQ__Connections__Default__Password=test
|
||||
# $env:RabbitMQ__Connections__Default__Port=5672
|
||||
# $env:RabbitMQ__Connections__Default__UserName=test
|
||||
# $env:RabbitMQ__EventBus__ExchangeName=MyTelegramExchange
|
||||
|
||||
# Redis
|
||||
# $env:Redis__Configuration=localhost:6379
|
||||
|
||||
# MongoDB
|
||||
# $env:ConnectionStrings__Default=mongodb://localhost:27017
|
||||
|
||||
# Minio
|
||||
# $env:Minio__AccessKey=test
|
||||
# $env:Minio__BucketName=tg-files
|
||||
# $env:Minio__CreateBucketIfNotExists=True
|
||||
# $env:Minio__Endpoint=localhost:9000
|
||||
# $env:Minio__SecretKey=12345678
|
||||
|
||||
# TwilioSms
|
||||
# $env:TwilioSms__AccountSId=
|
||||
# $env:TwilioSms__AuthToken=
|
||||
# $env:TwilioSms__Enabled=False
|
||||
# $env:TwilioSms__FromNumber=
|
||||
# VonageSms
|
||||
# $env:VonageSms__ApiKey=
|
||||
# $env:VonageSms__ApiSecret=
|
||||
# $env:VonageSms__BrandName=
|
||||
# $env:VonageSms__Enabled=False
|
||||
|
||||
# App
|
||||
# $env:App=
|
||||
# $env:App__AllowedUserIds__0=2000001
|
||||
# $env:App__AutoCreateSuperGroup=True
|
||||
# $env:App__BotDatabaseName=tg
|
||||
# $env:App__ChannelGetDifferenceIntervalSeconds=60
|
||||
# $env:App__CommandServerObjectIds=
|
||||
# $env:App__DatabaseName=tg
|
||||
# $env:App__DcOptions__0__Cdn=False
|
||||
# $env:App__DcOptions__0__Enabled=True
|
||||
# $env:App__DcOptions__0__Id=1
|
||||
# $env:App__DcOptions__0__IpAddress=192.168.1.100
|
||||
# $env:App__DcOptions__0__Ipv6=False
|
||||
# $env:App__DcOptions__0__MediaOnly=False
|
||||
# $env:App__DcOptions__0__Port=20443
|
||||
# $env:App__DcOptions__0__Static=False
|
||||
# $env:App__DcOptions__0__TcpoOnly=True
|
||||
# $env:App__DcOptions__0__ThisPortOnly=True
|
||||
# $env:App__DcOptions__1__Cdn=False
|
||||
# $env:App__DcOptions__1__Enabled=True
|
||||
# $env:App__DcOptions__1__Id=2
|
||||
# $env:App__DcOptions__1__IpAddress=192.168.1.100
|
||||
# $env:App__DcOptions__1__Ipv6=False
|
||||
# $env:App__DcOptions__1__MediaOnly=False
|
||||
# $env:App__DcOptions__1__Port=20543
|
||||
# $env:App__DcOptions__1__Static=False
|
||||
# $env:App__DcOptions__1__TcpoOnly=True
|
||||
# $env:App__DcOptions__1__ThisPortOnly=True
|
||||
# $env:App__DcOptions__2__Cdn=False
|
||||
# $env:App__DcOptions__2__Enabled=True
|
||||
# $env:App__DcOptions__2__Id=2
|
||||
# $env:App__DcOptions__2__IpAddress=192.168.1.100
|
||||
# $env:App__DcOptions__2__Ipv6=False
|
||||
# $env:App__DcOptions__2__MediaOnly=True
|
||||
# $env:App__DcOptions__2__Port=20643
|
||||
# $env:App__DcOptions__2__Static=False
|
||||
# $env:App__DcOptions__2__TcpoOnly=True
|
||||
# $env:App__DcOptions__2__ThisPortOnly=True
|
||||
# $env:App__DcOptions__3__Cdn=False
|
||||
# $env:App__DcOptions__3__Enabled=True
|
||||
# $env:App__DcOptions__3__Id=2
|
||||
# $env:App__DcOptions__3__IpAddress=192.168.1.100
|
||||
# $env:App__DcOptions__3__Ipv6=False
|
||||
# $env:App__DcOptions__3__MediaOnly=True
|
||||
# $env:App__DcOptions__3__Port=20644
|
||||
# $env:App__DcOptions__3__Static=False
|
||||
# $env:App__DcOptions__3__TcpoOnly=True
|
||||
# $env:App__DcOptions__3__ThisPortOnly=True
|
||||
# $env:App__DispatchUnknownObjectToMessengerServer=True
|
||||
# $env:App__EnableFutureAuthToken=True
|
||||
# $env:App__FileServerGrpcServiceUrl=http://localhost:10001
|
||||
# $env:App__FixedVerifyCode=
|
||||
# $env:App__IdGeneratorGrpcServiceUrl=http://localhost:10002
|
||||
# $env:App__IsMediaDc=False
|
||||
# $env:App__JoinChatDomain=https://t.me
|
||||
# $env:App__LoadAllStickersIntoMemory=True
|
||||
# $env:App__MediaDcId=2
|
||||
# $env:App__MediaOnly=False
|
||||
# $env:App__MessengerServerGrpcServiceUrl=http://localhost:10003
|
||||
# $env:App__OfficialTelegramBotOptions__ProxyUrl=socks5://localhost:10808
|
||||
# $env:App__OfficialTelegramBotOptions__Token=
|
||||
# $env:App__OfficialTelegramBotOptions__UseProxy=True
|
||||
# $env:App__PrivateKeyFilePath=private.pkcs8.key
|
||||
# $env:App__QueryServerEventStoreDatabaseName=tg-1
|
||||
# $env:App__QueryServerReadModelDatabaseName=tg-1
|
||||
# $env:App__SendWelcomeMessageAfterUserSignIn=False
|
||||
# $env:App__Servers__0__CertPemFilePath=
|
||||
# $env:App__Servers__0__Enabled=True
|
||||
# $env:App__Servers__0__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__0__Ip=
|
||||
# $env:App__Servers__0__Ipv6=True
|
||||
# $env:App__Servers__0__KeyPemFilePath=
|
||||
# $env:App__Servers__0__MediaOnly=False
|
||||
# $env:App__Servers__0__Port=20443
|
||||
# $env:App__Servers__0__ServerType=0
|
||||
# $env:App__Servers__0__Ssl=False
|
||||
# $env:App__Servers__1__CertPemFilePath=
|
||||
# $env:App__Servers__1__Enabled=True
|
||||
# $env:App__Servers__1__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__1__Ip=
|
||||
# $env:App__Servers__1__Ipv6=True
|
||||
# $env:App__Servers__1__KeyPemFilePath=
|
||||
# $env:App__Servers__1__MediaOnly=False
|
||||
# $env:App__Servers__1__Port=20543
|
||||
# $env:App__Servers__1__ServerType=0
|
||||
# $env:App__Servers__1__Ssl=False
|
||||
# $env:App__Servers__2__CertPemFilePath=
|
||||
# $env:App__Servers__2__Enabled=False
|
||||
# $env:App__Servers__2__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__2__Ip=
|
||||
# $env:App__Servers__2__Ipv6=True
|
||||
# $env:App__Servers__2__KeyPemFilePath=
|
||||
# $env:App__Servers__2__MediaOnly=True
|
||||
# $env:App__Servers__2__Port=20643
|
||||
# $env:App__Servers__2__ServerType=0
|
||||
# $env:App__Servers__2__Ssl=False
|
||||
# $env:App__Servers__3__CertPemFilePath=
|
||||
# $env:App__Servers__3__Enabled=False
|
||||
# $env:App__Servers__3__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__3__Ip=
|
||||
# $env:App__Servers__3__Ipv6=True
|
||||
# $env:App__Servers__3__KeyPemFilePath=
|
||||
# $env:App__Servers__3__MediaOnly=True
|
||||
# $env:App__Servers__3__Port=20644
|
||||
# $env:App__Servers__3__ServerType=0
|
||||
# $env:App__Servers__3__Ssl=False
|
||||
# $env:App__Servers__4__CertPemFilePath=_wildcard.telegram2.com.pem
|
||||
# $env:App__Servers__4__Enabled=True
|
||||
# $env:App__Servers__4__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__4__Ip=
|
||||
# $env:App__Servers__4__Ipv6=True
|
||||
# $env:App__Servers__4__KeyPemFilePath=_wildcard.telegram2.com-key.pem
|
||||
# $env:App__Servers__4__MediaOnly=False
|
||||
# $env:App__Servers__4__Port=30443
|
||||
# $env:App__Servers__4__ServerType=1
|
||||
# $env:App__Servers__4__Ssl=True
|
||||
# $env:App__Servers__5__CertPemFilePath=
|
||||
# $env:App__Servers__5__Enabled=True
|
||||
# $env:App__Servers__5__EnableProxyProtocolV2=False
|
||||
# $env:App__Servers__5__Ip=
|
||||
# $env:App__Servers__5__Ipv6=True
|
||||
# $env:App__Servers__5__KeyPemFilePath=
|
||||
# $env:App__Servers__5__MediaOnly=False
|
||||
# $env:App__Servers__5__Port=30444
|
||||
# $env:App__Servers__5__ServerType=1
|
||||
# $env:App__Servers__5__Ssl=False
|
||||
# $env:App__SetPremiumToTrueAfterUserCreated=True
|
||||
# $env:App__SubscribeLocalRequest=True
|
||||
# $env:App__SubscribeRemoteRequest=True
|
||||
# $env:App__TempAuthKeyExpirationMinutes=720
|
||||
# $env:App__ThisDcId=1
|
||||
# $env:App__UploadRootPath=uploads2
|
||||
# $env:App__UseExternalWebHookSender=False
|
||||
# $env:App__UseInMemoryFilters=True
|
||||
# $env:App__VerificationCodeExpirationSeconds=300
|
||||
# $env:App__VerificationCodeLength=5
|
||||
# $env:App__WebHookSenderUrl=
|
||||
# $env:App__WebRtcConnections__0__Ip=192.168.1.100
|
||||
# $env:App__WebRtcConnections__0__Ipv6=
|
||||
# $env:App__WebRtcConnections__0__Password=b
|
||||
# $env:App__WebRtcConnections__0__Port=20444
|
||||
# $env:App__WebRtcConnections__0__Stun=True
|
||||
# $env:App__WebRtcConnections__0__Turn=True
|
||||
# $env:App__WebRtcConnections__0__UserName=a
|
||||
|
||||
Set-Location -Path ".\command-server"
|
||||
Start-Process -FilePath "MyTelegram.Messenger.CommandServer.exe"
|
||||
|
||||
Set-Location -Path "..\query-server"
|
||||
Start-Process -FilePath "MyTelegram.Messenger.QueryServer.exe"
|
||||
|
||||
Set-Location -Path "..\gateway"
|
||||
Start-Process -FilePath "MyTelegram.GatewayServer.exe"
|
||||
|
||||
<#
|
||||
Start-Process -FilePath "MyTelegram.GatewayServer.exe" -ArgumentList `
|
||||
"RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly", `
|
||||
"RabbitMQ:EventBus:ClientName=MyTelegramGatewayServer.MediaOnly", `
|
||||
"RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly", `
|
||||
"App:Servers:0:Enabled=false", `
|
||||
"App:Servers:1:Enabled=false", `
|
||||
"App:Servers:2:Enabled=true", `
|
||||
"App:Servers:3:Enabled=true", `
|
||||
"App:Servers:4:Enabled=false", `
|
||||
"App:Servers:5:Enabled=false"
|
||||
#>
|
||||
|
||||
Set-Location -Path "..\auth"
|
||||
Start-Process -FilePath "MyTelegram.AuthServer.exe"
|
||||
|
||||
<#
|
||||
Start-Process -FilePath "MyTelegram.AuthServer.exe" -ArgumentList `
|
||||
"RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly", `
|
||||
"RabbitMQ:EventBus:ClientName=MyTelegramAuthServer.MediaOnly", `
|
||||
"RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly"
|
||||
#>
|
||||
|
||||
Set-Location -Path "..\session"
|
||||
Start-Process -FilePath "MyTelegram.SessionServer.exe"
|
||||
|
||||
<#
|
||||
Start-Process -FilePath "MyTelegram.SessionServer.exe" -ArgumentList `
|
||||
"RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly", `
|
||||
"App:MediaOnly=true", `
|
||||
"RabbitMQ:EventBus:ClientName=MyTelegramSessionServer.MediaOnly", `
|
||||
"RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly"
|
||||
#>
|
||||
|
||||
Set-Location -Path "..\file"
|
||||
Start-Process -FilePath "MyTelegram.FileServer.exe" -ArgumentList "urls=http://.+:10001"
|
||||
|
||||
<#
|
||||
Start-Process -FilePath "MyTelegram.FileServer.exe" -ArgumentList `
|
||||
"urls=http://.+:10007", `
|
||||
"RabbitMQ:EventBus:ClientName=MyTelegramFileServer.MediaOnly", `
|
||||
"RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly"
|
||||
#>
|
||||
|
||||
Set-Location -Path "..\messenger-grpc-service"
|
||||
Start-Process -FilePath "MyTelegram.MessengerServer.GrpcService.exe" -ArgumentList "urls=http://.+:10003"
|
||||
|
||||
Set-Location -Path "..\sms"
|
||||
Start-Process -FilePath "MyTelegram.SmsSender.exe"
|
||||
|
||||
|
||||
Set-Location ../
|
||||
@@ -0,0 +1,240 @@
|
||||
#!/bin/sh
|
||||
# For more information,please visit https://github.com/loyldg/mytelegram
|
||||
export App__QueryServerEventStoreDatabaseName=tg-1
|
||||
export App__QueryServerReadModelDatabaseName=tg-1
|
||||
export App__DatabaseName=tg
|
||||
export App__BotDatabaseName=tg
|
||||
|
||||
export App__FixedVerifyCode=22222
|
||||
|
||||
export Serilog__MinimumLevel__Default=Information
|
||||
export Serilog__MinimumLevel__Override__Microsoft=Information
|
||||
|
||||
# Use separate media server
|
||||
export App__Servers__2__Enabled=True
|
||||
export App__Servers__3__Enabled=True
|
||||
|
||||
# RabbitMQ
|
||||
# export RabbitMQ__Connections__Default__HostName=localhost
|
||||
# export RabbitMQ__Connections__Default__Password=test
|
||||
# export RabbitMQ__Connections__Default__Port=5672
|
||||
# export RabbitMQ__Connections__Default__UserName=test
|
||||
# export RabbitMQ__EventBus__ExchangeName=MyTelegramExchange
|
||||
|
||||
# Redis
|
||||
# export Redis__Configuration=localhost:6379
|
||||
|
||||
# MongoDB
|
||||
# export ConnectionStrings__Default=mongodb://localhost:27017
|
||||
|
||||
# Minio
|
||||
# export Minio__AccessKey=test
|
||||
# export Minio__BucketName=tg-files
|
||||
# export Minio__CreateBucketIfNotExists=True
|
||||
# export Minio__Endpoint=localhost:9000
|
||||
# export Minio__SecretKey=12345678
|
||||
|
||||
# TwilioSms
|
||||
# export TwilioSms__AccountSId=
|
||||
# export TwilioSms__AuthToken=
|
||||
# export TwilioSms__Enabled=False
|
||||
# export TwilioSms__FromNumber=
|
||||
# VonageSms
|
||||
# export VonageSms__ApiKey=
|
||||
# export VonageSms__ApiSecret=
|
||||
# export VonageSms__BrandName=
|
||||
# export VonageSms__Enabled=False
|
||||
|
||||
# App
|
||||
# export App__AllowedUserIds__0=2000001
|
||||
# export App__AutoCreateSuperGroup=True
|
||||
# export App__BotDatabaseName=tg
|
||||
# export App__ChannelGetDifferenceIntervalSeconds=60
|
||||
# export App__CommandServerObjectIds=
|
||||
# export App__DatabaseName=tg
|
||||
# export App__DcOptions__0__Cdn=False
|
||||
# export App__DcOptions__0__Enabled=True
|
||||
# export App__DcOptions__0__Id=1
|
||||
# export App__DcOptions__0__IpAddress=192.168.1.100
|
||||
# export App__DcOptions__0__Ipv6=False
|
||||
# export App__DcOptions__0__MediaOnly=False
|
||||
# export App__DcOptions__0__Port=20443
|
||||
# export App__DcOptions__0__Static=False
|
||||
# export App__DcOptions__0__TcpoOnly=True
|
||||
# export App__DcOptions__0__ThisPortOnly=True
|
||||
# export App__DcOptions__1__Cdn=False
|
||||
# export App__DcOptions__1__Enabled=True
|
||||
# export App__DcOptions__1__Id=2
|
||||
# export App__DcOptions__1__IpAddress=192.168.1.100
|
||||
# export App__DcOptions__1__Ipv6=False
|
||||
# export App__DcOptions__1__MediaOnly=False
|
||||
# export App__DcOptions__1__Port=20543
|
||||
# export App__DcOptions__1__Static=False
|
||||
# export App__DcOptions__1__TcpoOnly=True
|
||||
# export App__DcOptions__1__ThisPortOnly=True
|
||||
# export App__DcOptions__2__Cdn=False
|
||||
# export App__DcOptions__2__Enabled=True
|
||||
# export App__DcOptions__2__Id=2
|
||||
# export App__DcOptions__2__IpAddress=192.168.1.100
|
||||
# export App__DcOptions__2__Ipv6=False
|
||||
# export App__DcOptions__2__MediaOnly=True
|
||||
# export App__DcOptions__2__Port=20643
|
||||
# export App__DcOptions__2__Static=False
|
||||
# export App__DcOptions__2__TcpoOnly=True
|
||||
# export App__DcOptions__2__ThisPortOnly=True
|
||||
# export App__DcOptions__3__Cdn=False
|
||||
# export App__DcOptions__3__Enabled=True
|
||||
# export App__DcOptions__3__Id=2
|
||||
# export App__DcOptions__3__IpAddress=192.168.1.100
|
||||
# export App__DcOptions__3__Ipv6=False
|
||||
# export App__DcOptions__3__MediaOnly=True
|
||||
# export App__DcOptions__3__Port=20644
|
||||
# export App__DcOptions__3__Static=False
|
||||
# export App__DcOptions__3__TcpoOnly=True
|
||||
# export App__DcOptions__3__ThisPortOnly=True
|
||||
# export App__DispatchUnknownObjectToMessengerServer=True
|
||||
# export App__EnableFutureAuthToken=True
|
||||
# export App__FileServerGrpcServiceUrl=http://localhost:10001
|
||||
# export App__FixedVerifyCode=
|
||||
# export App__IdGeneratorGrpcServiceUrl=http://localhost:10002
|
||||
# export App__IsMediaDc=False
|
||||
# export App__JoinChatDomain=https://t.me
|
||||
# export App__LoadAllStickersIntoMemory=True
|
||||
# export App__MediaDcId=2
|
||||
# export App__MediaOnly=False
|
||||
# export App__MessengerServerGrpcServiceUrl=http://localhost:10003
|
||||
# export App__OfficialTelegramBotOptions__ProxyUrl=socks5://localhost:10808
|
||||
# export App__OfficialTelegramBotOptions__Token=
|
||||
# export App__OfficialTelegramBotOptions__UseProxy=True
|
||||
# export App__PrivateKeyFilePath=private.pkcs8.key
|
||||
# export App__QueryServerEventStoreDatabaseName=tg-1
|
||||
# export App__QueryServerReadModelDatabaseName=tg-1
|
||||
# export App__SendWelcomeMessageAfterUserSignIn=False
|
||||
# export App__Servers__0__CertPemFilePath=
|
||||
# export App__Servers__0__Enabled=True
|
||||
# export App__Servers__0__EnableProxyProtocolV2=False
|
||||
# export App__Servers__0__Ip=
|
||||
# export App__Servers__0__Ipv6=True
|
||||
# export App__Servers__0__KeyPemFilePath=
|
||||
# export App__Servers__0__MediaOnly=False
|
||||
# export App__Servers__0__Port=20443
|
||||
# export App__Servers__0__ServerType=0
|
||||
# export App__Servers__0__Ssl=False
|
||||
# export App__Servers__1__CertPemFilePath=
|
||||
# export App__Servers__1__Enabled=True
|
||||
# export App__Servers__1__EnableProxyProtocolV2=False
|
||||
# export App__Servers__1__Ip=
|
||||
# export App__Servers__1__Ipv6=True
|
||||
# export App__Servers__1__KeyPemFilePath=
|
||||
# export App__Servers__1__MediaOnly=False
|
||||
# export App__Servers__1__Port=20543
|
||||
# export App__Servers__1__ServerType=0
|
||||
# export App__Servers__1__Ssl=False
|
||||
# export App__Servers__2__CertPemFilePath=
|
||||
# export App__Servers__2__Enabled=False
|
||||
# export App__Servers__2__EnableProxyProtocolV2=False
|
||||
# export App__Servers__2__Ip=
|
||||
# export App__Servers__2__Ipv6=True
|
||||
# export App__Servers__2__KeyPemFilePath=
|
||||
# export App__Servers__2__MediaOnly=True
|
||||
# export App__Servers__2__Port=20643
|
||||
# export App__Servers__2__ServerType=0
|
||||
# export App__Servers__2__Ssl=False
|
||||
# export App__Servers__3__CertPemFilePath=
|
||||
# export App__Servers__3__Enabled=False
|
||||
# export App__Servers__3__EnableProxyProtocolV2=False
|
||||
# export App__Servers__3__Ip=
|
||||
# export App__Servers__3__Ipv6=True
|
||||
# export App__Servers__3__KeyPemFilePath=
|
||||
# export App__Servers__3__MediaOnly=True
|
||||
# export App__Servers__3__Port=20644
|
||||
# export App__Servers__3__ServerType=0
|
||||
# export App__Servers__3__Ssl=False
|
||||
# export App__Servers__4__CertPemFilePath=_wildcard.telegram2.com.pem
|
||||
# export App__Servers__4__Enabled=True
|
||||
# export App__Servers__4__EnableProxyProtocolV2=False
|
||||
# export App__Servers__4__Ip=
|
||||
# export App__Servers__4__Ipv6=True
|
||||
# export App__Servers__4__KeyPemFilePath=_wildcard.telegram2.com-key.pem
|
||||
# export App__Servers__4__MediaOnly=False
|
||||
# export App__Servers__4__Port=30443
|
||||
# export App__Servers__4__ServerType=1
|
||||
# export App__Servers__4__Ssl=True
|
||||
# export App__Servers__5__CertPemFilePath=
|
||||
# export App__Servers__5__Enabled=True
|
||||
# export App__Servers__5__EnableProxyProtocolV2=False
|
||||
# export App__Servers__5__Ip=
|
||||
# export App__Servers__5__Ipv6=True
|
||||
# export App__Servers__5__KeyPemFilePath=
|
||||
# export App__Servers__5__MediaOnly=False
|
||||
# export App__Servers__5__Port=30444
|
||||
# export App__Servers__5__ServerType=1
|
||||
# export App__Servers__5__Ssl=False
|
||||
# export App__SetPremiumToTrueAfterUserCreated=True
|
||||
# export App__SubscribeLocalRequest=True
|
||||
# export App__SubscribeRemoteRequest=True
|
||||
# export App__TempAuthKeyExpirationMinutes=720
|
||||
# export App__ThisDcId=1
|
||||
# export App__UploadRootPath=uploads2
|
||||
# export App__UseExternalWebHookSender=False
|
||||
# export App__UseInMemoryFilters=True
|
||||
# export App__VerificationCodeExpirationSeconds=300
|
||||
# export App__VerificationCodeLength=5
|
||||
# export App__WebHookSenderUrl=
|
||||
# export App__WebRtcConnections__0__Ip=192.168.1.100
|
||||
# export App__WebRtcConnections__0__Ipv6=
|
||||
# export App__WebRtcConnections__0__Password=b
|
||||
# export App__WebRtcConnections__0__Port=20444
|
||||
# export App__WebRtcConnections__0__Stun=True
|
||||
# export App__WebRtcConnections__0__Turn=True
|
||||
# export App__WebRtcConnections__0__UserName=a
|
||||
|
||||
|
||||
|
||||
cd ./command-server
|
||||
./MyTelegram.Messenger.CommandServer &
|
||||
|
||||
cd ../query-server
|
||||
./MyTelegram.Messenger.QueryServer &
|
||||
|
||||
cd ../gateway
|
||||
./MyTelegram.GatewayServer &
|
||||
|
||||
# ./MyTelegram.GatewayServer \
|
||||
# RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly \
|
||||
# RabbitMQ:EventBus:ClientName=MyTelegramGatewayServer.MediaOnly \
|
||||
# RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly \
|
||||
# App:Servers:0:Enabled=false \
|
||||
# App:Servers:1:Enabled=false \
|
||||
# App:Servers:2:Enabled=true \
|
||||
# App:Servers:3:Enabled=true \
|
||||
# App:Servers:4:Enabled=false \
|
||||
# App:Servers:5:Enabled=false &
|
||||
|
||||
cd ../auth
|
||||
./MyTelegram.AuthServer &
|
||||
|
||||
# ./MyTelegram.AuthServer \
|
||||
# RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly \
|
||||
# RabbitMQ:EventBus:ClientName=MyTelegramAuthServer.MediaOnly \
|
||||
# RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly &
|
||||
|
||||
cd ../session
|
||||
./MyTelegram.SessionServer &
|
||||
# ./MyTelegram.SessionServer \
|
||||
# RabbitMQ:EventBus:ExchangeName=MyTelegramExchange.MediaOnly \
|
||||
# App:MediaOnly=true \
|
||||
# RabbitMQ:EventBus:ClientName=MyTelegramSessionServer.MediaOnly \
|
||||
# RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly &
|
||||
|
||||
cd ../file
|
||||
./MyTelegram.FileServer urls=http://.+:10001 &
|
||||
# ./MyTelegram.FileServer.exe urls=http://.+:10007 \
|
||||
# RabbitMQ:EventBus:ClientName=MyTelegramFileServer.MediaOnly \
|
||||
# RabbitMQ:EventBus:TopicExchangeName=MyTelegramTopicExchange.MediaOnly &
|
||||
|
||||
cd ../messenger-grpc
|
||||
./MyTelegram.MessengerServer.GrpcService urls=http://.+:10003 &
|
||||
|
||||
cd ../sms
|
||||
./MyTelegram.SmsSender &
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
taskkill /im MyTelegram.Messenger.CommandServer.exe /f
|
||||
taskkill /im MyTelegram.Messenger.QueryServer.exe /f
|
||||
taskkill /im MyTelegram.GatewayServer.exe /f
|
||||
taskkill /im MyTelegram.AuthServer.exe /f
|
||||
taskkill /im MyTelegram.SessionServer.exe /f
|
||||
taskkill /im MyTelegram.FileServer.exe /f
|
||||
taskkill /im MyTelegram.MessengerServer.GrpcService.exe /f
|
||||
taskkill /im MyTelegram.MessengerServer.exe /f
|
||||
taskkill /im MyTelegram.SmsSender.exe /f
|
||||
@@ -0,0 +1,30 @@
|
||||
# Скрипт PowerShell для проверки Bot API
|
||||
$TOKEN = "999999999:ABCdefGHIjklMNOpqrsTUVwxyz123456"
|
||||
$BASE_URL = "http://localhost:8081/bot$TOKEN"
|
||||
|
||||
Write-Host "Testing MyTelegram Bot API" -ForegroundColor Cyan
|
||||
Write-Host "================================" -ForegroundColor Cyan
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "1. Testing getMe..." -ForegroundColor Yellow
|
||||
Invoke-RestMethod -Uri "$BASE_URL/getMe" | ConvertTo-Json
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "2. Testing getUpdates..." -ForegroundColor Yellow
|
||||
Invoke-RestMethod -Uri "$BASE_URL/getUpdates" | ConvertTo-Json
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "3. Testing getWebhookInfo..." -ForegroundColor Yellow
|
||||
Invoke-RestMethod -Uri "$BASE_URL/getWebhookInfo" | ConvertTo-Json
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "4. Testing sendMessage..." -ForegroundColor Yellow
|
||||
$body = @{
|
||||
chat_id = 2010001
|
||||
text = "Hello from test bot!"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Invoke-RestMethod -Uri "$BASE_URL/sendMessage" -Method Post -Body $body -ContentType "application/json" | ConvertTo-Json
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Tests completed" -ForegroundColor Green
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Скрипт для проверки Bot API
|
||||
TOKEN="999999999:ABCdefGHIjklMNOpqrsTUVwxyz123456"
|
||||
BASE_URL="http://localhost:8081/bot$TOKEN"
|
||||
|
||||
echo "Testing MyTelegram Bot API"
|
||||
echo "================================"
|
||||
|
||||
echo ""
|
||||
echo "1. Testing getMe..."
|
||||
curl -s "$BASE_URL/getMe" | jq .
|
||||
|
||||
echo ""
|
||||
echo "2. Testing getUpdates..."
|
||||
curl -s "$BASE_URL/getUpdates" | jq .
|
||||
|
||||
echo ""
|
||||
echo "3. Testing getWebhookInfo..."
|
||||
curl -s "$BASE_URL/getWebhookInfo" | jq .
|
||||
|
||||
echo ""
|
||||
echo "4. Testing sendMessage..."
|
||||
curl -s -X POST "$BASE_URL/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"chat_id": 2010001, "text": "Hello from test bot!"}' | jq .
|
||||
|
||||
echo ""
|
||||
echo "Tests completed"
|
||||
@@ -0,0 +1,25 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
@@ -0,0 +1,60 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- <VersionSuffix>0.6.$([System.DateTime]::Now.ToString(MMdd)).$([System.DateTime]::Now.ToString("HHmm"))</VersionSuffix> -->
|
||||
<VersionSuffix>0.32.206.$([System.DateTime]::Now.ToString("Mdd"))</VersionSuffix>
|
||||
<AssemblyVersion Condition="'$(VersionSuffix)' =='' ">0.0.0.1</AssemblyVersion>
|
||||
<AssemblyVersion Condition="'$(VersionSuffix)' !='' ">$(VersionSuffix)</AssemblyVersion>
|
||||
<Version Condition="'$(VersionSuffix)' =='' ">0.0.1.0</Version>
|
||||
<Version Condition="'$(VersionSuffix)' !='' ">$(VersionSuffix)</Version>
|
||||
<NoWarn>$(NoWarn);CS1591;CS0436;NU1803;</NoWarn>
|
||||
<Authors>MyTelegram</Authors>
|
||||
<RepositoryUrl>https://github.com/loyldg/mytelegram</RepositoryUrl>
|
||||
<Company>MyTelegram</Company>
|
||||
<Copyright>Copyright (c) MyTelegram 2021 - $([System.DateTime]::Now.ToString("yyyy"))</Copyright>
|
||||
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<!--
|
||||
<PropertyGroup>
|
||||
<OutputPath>$(MSBuildThisFileDirectory)out\local\$(AssemblyVersion)\$(Configuration)\$(MSBuildProjectName.Replace("MyTelegram.", ""))</OutputPath>
|
||||
</PropertyGroup> -->
|
||||
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<!--
|
||||
<PropertyGroup>
|
||||
<DebuggerSupport>false</DebuggerSupport>
|
||||
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
|
||||
<EventSourceSupport>false</EventSourceSupport>
|
||||
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>link</TrimMode>
|
||||
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
|
||||
</PropertyGroup>
|
||||
-->
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ConfigureAwait.Fody" PrivateAssets="All" />
|
||||
<PackageReference Include="Fody" >
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,64 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" />
|
||||
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
|
||||
<PackageVersion Include="BloomFilter.NetCore" Version="2.1.1" />
|
||||
<PackageVersion Include="CommunityToolkit.HighPerformance" Version="8.4.0" />
|
||||
<PackageVersion Include="ConfigureAwait.Fody" PrivateAssets="All" Version="3.3.2" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageVersion Include="Cube.Timer" Version="1.0.5" />
|
||||
<PackageVersion Include="EventFlow" Version="1.2.1" />
|
||||
<PackageVersion Include="EventFlow.MongoDB" Version="1.2.1" />
|
||||
<PackageVersion Include="Fody" Version="6.9.2" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.31.1" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.72.0" />
|
||||
<PackageVersion Include="HashedWheelTimer" Version="1.0.3" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.22.1-Preview.1" />
|
||||
<PackageVersion Include="MessagePack" Version="2.5.192" />
|
||||
<PackageVersion Include="Minio" Version="6.0.3" />
|
||||
<PackageVersion Include="MongoDB.Driver" Version="3.0.0" />
|
||||
<PackageVersion Include="Polly" Version="8.6.2" />
|
||||
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
|
||||
<PackageVersion Include="Rebus" Version="8.8.0" />
|
||||
<PackageVersion Include="Rebus.Kafka" Version="2.0.0" />
|
||||
<PackageVersion Include="Rebus.MsgPack" Version="7.0.0" />
|
||||
<PackageVersion Include="Rebus.RabbitMq" Version="10.0.1" />
|
||||
<PackageVersion Include="Rebus.ServiceProvider" Version="10.3.0" />
|
||||
<PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
||||
<PackageVersion Include="Sigil" Version="5.0.0" />
|
||||
<PackageVersion Include="Standart.Hash.xxHash" Version="4.0.5" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageVersion Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="Twilio" Version="7.12.0" />
|
||||
<PackageVersion Include="Vonage" Version="8.2.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,289 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.2.32210.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2DA560C9-CF55-4AE8-9876-035A24D70098}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{91ACB8C4-58D1-42AC-A233-248BC18925B7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Schema", "src\MyTelegram.Schema\MyTelegram.Schema.csproj", "{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Schema.Tests", "test\MyTelegram.Schema.Tests\MyTelegram.Schema.Tests.csproj", "{F86A6EBD-7FCC-4349-865B-BADF2AC331C2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Domain", "Domain", "{EB17BE1D-A92C-49B5-BFF1-DD75BC7DC94B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{DB4DF262-E785-497F-B465-440175026F97}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Core", "src\MyTelegram.Core\MyTelegram.Core.csproj", "{77D2BB76-B7AF-4200-B799-F19F879F7CB4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Domain", "src\MyTelegram.Domain\MyTelegram.Domain.csproj", "{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Domain.Shared", "src\MyTelegram.Domain.Shared\MyTelegram.Domain.Shared.csproj", "{117B0121-B9E5-4481-8054-F33D1DA86AEC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.GrpcService", "src\MyTelegram.GrpcService\MyTelegram.GrpcService.csproj", "{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Queries", "src\MyTelegram.Queries\MyTelegram.Queries.csproj", "{6BCC94F3-717C-42EB-B051-DCFD15C9A068}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.ReadModel", "src\MyTelegram.ReadModel\MyTelegram.ReadModel.csproj", "{7602FA14-87A9-490E-91E2-BBC0633FE194}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.ReadModel.MongoDB", "src\MyTelegram.ReadModel.MongoDB\MyTelegram.ReadModel.MongoDB.csproj", "{1B3840FD-9C14-489F-8E03-DFEDA01207CF}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReadModel", "ReadModel", "{3BF8F0CE-05C3-4386-B960-C56F6719D294}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Domain.Tests", "test\MyTelegram.Domain.Tests\MyTelegram.Domain.Tests.csproj", "{1076A82B-A813-44A5-93F6-4CCA1C15090E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{5F3FB626-5C24-4663-9B74-C9257417305E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Cuckoo", "src\Microsoft.Cuckoo\Microsoft.Cuckoo.csproj", "{9B208770-781B-42B0-8F2A-E87B0D5341AD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.ReadModel.InMemory", "src\MyTelegram.ReadModel.InMemory\MyTelegram.ReadModel.InMemory.csproj", "{11F1970C-D03D-4153-B27E-C4774AB4509A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Domain.IntegrationTests", "test\MyTelegram.Domain.IntegrationTests\MyTelegram.Domain.IntegrationTests.csproj", "{BF85677F-198E-4CAE-A13D-38CA555C9F76}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.TestBase", "src\MyTelegram.TestBase\MyTelegram.TestBase.csproj", "{D6842BB0-435B-408A-8040-11FAD9479E79}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.QueryHandlers.InMemory", "src\MyTelegram.QueryHandlers.InMemory\MyTelegram.QueryHandlers.InMemory.csproj", "{65D57042-EE58-4EBC-A5EE-B618BB99641D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.SmsSender", "src\MyTelegram.SmsSender\MyTelegram.SmsSender.csproj", "{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.EventBus", "src\MyTelegram.EventBus\MyTelegram.EventBus.csproj", "{F23531FF-582D-48BA-988A-C484B86B44B2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.EventFlow.MongoDB", "src\MyTelegram.EventFlow.MongoDB\MyTelegram.EventFlow.MongoDB.csproj", "{175B77B0-25DB-47C4-881B-DEC3FAADF78A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Caching.Redis", "src\MyTelegram.Caching.Redis\MyTelegram.Caching.Redis.csproj", "{7F5612B7-188E-42D1-9F41-3CC32062519B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.MTProto", "src\MyTelegram.MTProto\MyTelegram.MTProto.csproj", "{44C09B7D-9917-4AFE-809C-2DA8E93048DD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.MTProto.Tests", "test\MyTelegram.MTProto.Tests\MyTelegram.MTProto.Tests.csproj", "{816A9714-67FD-42BA-8411-3AD9B0A90701}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.GatewayServer", "src\MyTelegram.GatewayServer\MyTelegram.GatewayServer.csproj", "{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Messenger", "src\MyTelegram.Messenger\MyTelegram.Messenger.csproj", "{9967162A-3ABF-44A9-B796-D1925B257805}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Messenger.CommandServer", "src\MyTelegram.Messenger.CommandServer\MyTelegram.Messenger.CommandServer.csproj", "{18483359-47B7-4FB7-9BAE-F598105B15F5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Messenger.QueryServer", "src\MyTelegram.Messenger.QueryServer\MyTelegram.Messenger.QueryServer.csproj", "{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.Services", "src\MyTelegram.Services\MyTelegram.Services.csproj", "{0A9D2AE0-F943-408E-B740-2101DDF865CE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.EventFlow", "src\MyTelegram.EventFlow\MyTelegram.EventFlow.csproj", "{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.TestApp", "src\MyTelegram.TestApp\MyTelegram.TestApp.csproj", "{8D070E04-ABCE-439F-A7A9-864D73225D34}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTelegram.AuthServer", "src\MyTelegram.AuthServer\MyTelegram.AuthServer.csproj", "{A45E8362-191B-4CF4-BC0C-89F81C5A17FE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.ReadModel.Interfaces", "src\MyTelegram.ReadModel.Interfaces\MyTelegram.ReadModel.Interfaces.csproj", "{A5AD515A-B43E-C625-1B44-589AAA14686C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.Services.Tests", "test\MyTelegram.Services.Tests\MyTelegram.Services.Tests.csproj", "{8CD9759A-0026-33FC-DE4B-6BE3229B5109}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.QueryHandlers.MongoDB", "src\MyTelegram.QueryHandlers.MongoDB\MyTelegram.QueryHandlers.MongoDB.csproj", "{710834A8-C271-4853-7B3D-1661A2535D9C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.Converters", "src\MyTelegram.Converters\MyTelegram.Converters.csproj", "{2970172B-D8E3-F5AC-ABE1-F96E39F15A80}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.DataSeeder", "src\MyTelegram.DataSeeder\MyTelegram.DataSeeder.csproj", "{AE915013-50A2-6610-F705-53059D40139B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.Abstractions", "src\MyTelegram.Abstractions\MyTelegram.Abstractions.csproj", "{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyTelegram.EventBus.RabbitMQ", "src\MyTelegram.EventBus.RabbitMQ\MyTelegram.EventBus.RabbitMQ.csproj", "{87FBEAB7-2F9A-EAAC-084E-F9408151E00B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F86A6EBD-7FCC-4349-865B-BADF2AC331C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F86A6EBD-7FCC-4349-865B-BADF2AC331C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F86A6EBD-7FCC-4349-865B-BADF2AC331C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F86A6EBD-7FCC-4349-865B-BADF2AC331C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{77D2BB76-B7AF-4200-B799-F19F879F7CB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{77D2BB76-B7AF-4200-B799-F19F879F7CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77D2BB76-B7AF-4200-B799-F19F879F7CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77D2BB76-B7AF-4200-B799-F19F879F7CB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{117B0121-B9E5-4481-8054-F33D1DA86AEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{117B0121-B9E5-4481-8054-F33D1DA86AEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{117B0121-B9E5-4481-8054-F33D1DA86AEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{117B0121-B9E5-4481-8054-F33D1DA86AEC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6BCC94F3-717C-42EB-B051-DCFD15C9A068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6BCC94F3-717C-42EB-B051-DCFD15C9A068}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6BCC94F3-717C-42EB-B051-DCFD15C9A068}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6BCC94F3-717C-42EB-B051-DCFD15C9A068}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7602FA14-87A9-490E-91E2-BBC0633FE194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7602FA14-87A9-490E-91E2-BBC0633FE194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7602FA14-87A9-490E-91E2-BBC0633FE194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7602FA14-87A9-490E-91E2-BBC0633FE194}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1B3840FD-9C14-489F-8E03-DFEDA01207CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1B3840FD-9C14-489F-8E03-DFEDA01207CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1B3840FD-9C14-489F-8E03-DFEDA01207CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1B3840FD-9C14-489F-8E03-DFEDA01207CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1076A82B-A813-44A5-93F6-4CCA1C15090E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1076A82B-A813-44A5-93F6-4CCA1C15090E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1076A82B-A813-44A5-93F6-4CCA1C15090E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1076A82B-A813-44A5-93F6-4CCA1C15090E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B208770-781B-42B0-8F2A-E87B0D5341AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B208770-781B-42B0-8F2A-E87B0D5341AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B208770-781B-42B0-8F2A-E87B0D5341AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B208770-781B-42B0-8F2A-E87B0D5341AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{11F1970C-D03D-4153-B27E-C4774AB4509A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11F1970C-D03D-4153-B27E-C4774AB4509A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11F1970C-D03D-4153-B27E-C4774AB4509A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11F1970C-D03D-4153-B27E-C4774AB4509A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BF85677F-198E-4CAE-A13D-38CA555C9F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BF85677F-198E-4CAE-A13D-38CA555C9F76}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BF85677F-198E-4CAE-A13D-38CA555C9F76}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BF85677F-198E-4CAE-A13D-38CA555C9F76}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D6842BB0-435B-408A-8040-11FAD9479E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6842BB0-435B-408A-8040-11FAD9479E79}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D6842BB0-435B-408A-8040-11FAD9479E79}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D6842BB0-435B-408A-8040-11FAD9479E79}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{65D57042-EE58-4EBC-A5EE-B618BB99641D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{65D57042-EE58-4EBC-A5EE-B618BB99641D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{65D57042-EE58-4EBC-A5EE-B618BB99641D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{65D57042-EE58-4EBC-A5EE-B618BB99641D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F23531FF-582D-48BA-988A-C484B86B44B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F23531FF-582D-48BA-988A-C484B86B44B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F23531FF-582D-48BA-988A-C484B86B44B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F23531FF-582D-48BA-988A-C484B86B44B2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{175B77B0-25DB-47C4-881B-DEC3FAADF78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{175B77B0-25DB-47C4-881B-DEC3FAADF78A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{175B77B0-25DB-47C4-881B-DEC3FAADF78A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{175B77B0-25DB-47C4-881B-DEC3FAADF78A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F5612B7-188E-42D1-9F41-3CC32062519B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F5612B7-188E-42D1-9F41-3CC32062519B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F5612B7-188E-42D1-9F41-3CC32062519B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F5612B7-188E-42D1-9F41-3CC32062519B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{44C09B7D-9917-4AFE-809C-2DA8E93048DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{44C09B7D-9917-4AFE-809C-2DA8E93048DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{44C09B7D-9917-4AFE-809C-2DA8E93048DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{44C09B7D-9917-4AFE-809C-2DA8E93048DD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{816A9714-67FD-42BA-8411-3AD9B0A90701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{816A9714-67FD-42BA-8411-3AD9B0A90701}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{816A9714-67FD-42BA-8411-3AD9B0A90701}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{816A9714-67FD-42BA-8411-3AD9B0A90701}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9967162A-3ABF-44A9-B796-D1925B257805}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9967162A-3ABF-44A9-B796-D1925B257805}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9967162A-3ABF-44A9-B796-D1925B257805}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9967162A-3ABF-44A9-B796-D1925B257805}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{18483359-47B7-4FB7-9BAE-F598105B15F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{18483359-47B7-4FB7-9BAE-F598105B15F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{18483359-47B7-4FB7-9BAE-F598105B15F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{18483359-47B7-4FB7-9BAE-F598105B15F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A9D2AE0-F943-408E-B740-2101DDF865CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A9D2AE0-F943-408E-B740-2101DDF865CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A9D2AE0-F943-408E-B740-2101DDF865CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A9D2AE0-F943-408E-B740-2101DDF865CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8D070E04-ABCE-439F-A7A9-864D73225D34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8D070E04-ABCE-439F-A7A9-864D73225D34}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8D070E04-ABCE-439F-A7A9-864D73225D34}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8D070E04-ABCE-439F-A7A9-864D73225D34}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A45E8362-191B-4CF4-BC0C-89F81C5A17FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A45E8362-191B-4CF4-BC0C-89F81C5A17FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A45E8362-191B-4CF4-BC0C-89F81C5A17FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A45E8362-191B-4CF4-BC0C-89F81C5A17FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5AD515A-B43E-C625-1B44-589AAA14686C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5AD515A-B43E-C625-1B44-589AAA14686C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5AD515A-B43E-C625-1B44-589AAA14686C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5AD515A-B43E-C625-1B44-589AAA14686C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8CD9759A-0026-33FC-DE4B-6BE3229B5109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8CD9759A-0026-33FC-DE4B-6BE3229B5109}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8CD9759A-0026-33FC-DE4B-6BE3229B5109}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8CD9759A-0026-33FC-DE4B-6BE3229B5109}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{710834A8-C271-4853-7B3D-1661A2535D9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{710834A8-C271-4853-7B3D-1661A2535D9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{710834A8-C271-4853-7B3D-1661A2535D9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{710834A8-C271-4853-7B3D-1661A2535D9C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2970172B-D8E3-F5AC-ABE1-F96E39F15A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2970172B-D8E3-F5AC-ABE1-F96E39F15A80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2970172B-D8E3-F5AC-ABE1-F96E39F15A80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2970172B-D8E3-F5AC-ABE1-F96E39F15A80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AE915013-50A2-6610-F705-53059D40139B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AE915013-50A2-6610-F705-53059D40139B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AE915013-50A2-6610-F705-53059D40139B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AE915013-50A2-6610-F705-53059D40139B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87FBEAB7-2F9A-EAAC-084E-F9408151E00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87FBEAB7-2F9A-EAAC-084E-F9408151E00B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87FBEAB7-2F9A-EAAC-084E-F9408151E00B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87FBEAB7-2F9A-EAAC-084E-F9408151E00B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{635E30FB-0B64-41D9-ACCC-BD927FD1A2EB} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{F86A6EBD-7FCC-4349-865B-BADF2AC331C2} = {91ACB8C4-58D1-42AC-A233-248BC18925B7}
|
||||
{EB17BE1D-A92C-49B5-BFF1-DD75BC7DC94B} = {2DA560C9-CF55-4AE8-9876-035A24D70098}
|
||||
{DB4DF262-E785-497F-B465-440175026F97} = {2DA560C9-CF55-4AE8-9876-035A24D70098}
|
||||
{77D2BB76-B7AF-4200-B799-F19F879F7CB4} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{88DF870D-E5E7-4CC0-8FF0-60EF746C28AF} = {EB17BE1D-A92C-49B5-BFF1-DD75BC7DC94B}
|
||||
{117B0121-B9E5-4481-8054-F33D1DA86AEC} = {EB17BE1D-A92C-49B5-BFF1-DD75BC7DC94B}
|
||||
{B9DC33D2-34E6-4B6B-A22E-D7EFA1380004} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{6BCC94F3-717C-42EB-B051-DCFD15C9A068} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{7602FA14-87A9-490E-91E2-BBC0633FE194} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{1B3840FD-9C14-489F-8E03-DFEDA01207CF} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{3BF8F0CE-05C3-4386-B960-C56F6719D294} = {2DA560C9-CF55-4AE8-9876-035A24D70098}
|
||||
{1076A82B-A813-44A5-93F6-4CCA1C15090E} = {91ACB8C4-58D1-42AC-A233-248BC18925B7}
|
||||
{5F3FB626-5C24-4663-9B74-C9257417305E} = {2DA560C9-CF55-4AE8-9876-035A24D70098}
|
||||
{9B208770-781B-42B0-8F2A-E87B0D5341AD} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{11F1970C-D03D-4153-B27E-C4774AB4509A} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{BF85677F-198E-4CAE-A13D-38CA555C9F76} = {91ACB8C4-58D1-42AC-A233-248BC18925B7}
|
||||
{D6842BB0-435B-408A-8040-11FAD9479E79} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{65D57042-EE58-4EBC-A5EE-B618BB99641D} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{165F6836-4B8F-48E0-AFC4-8D2A0286F3F5} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{F23531FF-582D-48BA-988A-C484B86B44B2} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{175B77B0-25DB-47C4-881B-DEC3FAADF78A} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{7F5612B7-188E-42D1-9F41-3CC32062519B} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{44C09B7D-9917-4AFE-809C-2DA8E93048DD} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{816A9714-67FD-42BA-8411-3AD9B0A90701} = {91ACB8C4-58D1-42AC-A233-248BC18925B7}
|
||||
{5BFC0B2E-0DD2-48D4-AD4F-7245687C34F7} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{9967162A-3ABF-44A9-B796-D1925B257805} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{18483359-47B7-4FB7-9BAE-F598105B15F5} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{ACCB7A7F-19E2-4593-B6A0-97989D6B78E6} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{0A9D2AE0-F943-408E-B740-2101DDF865CE} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{D4E1CD1A-EA33-48B8-A965-D55F82CCFBC5} = {EB17BE1D-A92C-49B5-BFF1-DD75BC7DC94B}
|
||||
{8D070E04-ABCE-439F-A7A9-864D73225D34} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{A45E8362-191B-4CF4-BC0C-89F81C5A17FE} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{A5AD515A-B43E-C625-1B44-589AAA14686C} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{8CD9759A-0026-33FC-DE4B-6BE3229B5109} = {91ACB8C4-58D1-42AC-A233-248BC18925B7}
|
||||
{710834A8-C271-4853-7B3D-1661A2535D9C} = {3BF8F0CE-05C3-4386-B960-C56F6719D294}
|
||||
{2970172B-D8E3-F5AC-ABE1-F96E39F15A80} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{AE915013-50A2-6610-F705-53059D40139B} = {DB4DF262-E785-497F-B465-440175026F97}
|
||||
{81E53953-EFD5-F7BE-C844-FEC29CA7B3F7} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
{87FBEAB7-2F9A-EAAC-084E-F9408151E00B} = {5F3FB626-5C24-4663-9B74-C9257417305E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A8E7A112-1B80-4DCC-9884-5069D0E3B651}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<!-- <VersionSuffix>0.6.$([System.DateTime]::Now.ToString(MMdd)).$([System.DateTime]::Now.ToString("HHmm"))</VersionSuffix> -->
|
||||
<VersionSuffix>0.8.821.932</VersionSuffix>
|
||||
<AssemblyVersion Condition="'$(VersionSuffix)' =='' ">0.0.0.1</AssemblyVersion>
|
||||
<AssemblyVersion Condition="'$(VersionSuffix)' !='' ">$(VersionSuffix)</AssemblyVersion>
|
||||
<Version Condition="'$(VersionSuffix)' =='' ">0.0.1.0</Version>
|
||||
<Version Condition="'$(VersionSuffix)' !='' ">$(VersionSuffix)</Version>
|
||||
<NoWarn>$(NoWarn);CS1591;CS0436;NU1803;</NoWarn>
|
||||
<Authors>Loyldg</Authors>
|
||||
<RepositoryUrl>https://github.com/loyldg/mytelegram</RepositoryUrl>
|
||||
<Company>Loyldg</Company>
|
||||
<Copyright>Copyright (c) loyldg 2021 - 2023</Copyright>
|
||||
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<!-- `key` can be any identifier for your source. -->
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
|
||||
<!-- Define mappings by adding package patterns beneath the target source. -->
|
||||
<packageSourceMapping>
|
||||
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
@@ -0,0 +1,335 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CodegenHelpers.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Cuckoo.Benchmark")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.Cuckoo.Test")]
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Collection helper methods for generating code and comparators.
|
||||
/// </summary>
|
||||
internal static class CodegenHelpers
|
||||
{
|
||||
public static bool BytesEquals(byte[] a, byte[] b)
|
||||
{
|
||||
return BytesEquals(a, 0, b, 0, a.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the two arrays are equal. It assumes that both
|
||||
/// are at least the "length" long.
|
||||
/// </summary>
|
||||
/// <param name="a">First array</param>
|
||||
/// <param name="offsetA">Offset in first array to look from</param>
|
||||
/// <param name="b">Second array</param>
|
||||
/// <param name="offsetB">Offset in second array to look from</param>
|
||||
/// <param name="length">Number of bytes in each to compare</param>
|
||||
/// <returns>True if all bytes are equal, false otherwise</returns>
|
||||
public static bool BytesEquals(byte[] a, int offsetA, byte[] b, int offsetB, int length)
|
||||
{
|
||||
var baseOffset = 0;
|
||||
for (; baseOffset + Vector<byte>.Count <= length; baseOffset += Vector<byte>.Count)
|
||||
{
|
||||
var aVec = new Vector<byte>(a, baseOffset + offsetA);
|
||||
var bVec = new Vector<byte>(b, baseOffset + offsetB);
|
||||
if (aVec != bVec)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var remaining = length - baseOffset;
|
||||
for (var i = 0; i < remaining; i++)
|
||||
{
|
||||
if (a[baseOffset + offsetA + i] != b[baseOffset + offsetB + i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the array is all zeroes from the given offset
|
||||
/// and length.
|
||||
/// </summary>
|
||||
/// <param name="array">Array to check</param>
|
||||
/// <param name="offset">Offset to look at</param>
|
||||
/// <param name="length">Number of bytes to check</param>
|
||||
/// <returns>The number of bytes that are zero</returns>
|
||||
public static bool IsZero(byte[] array, int offset, int length)
|
||||
{
|
||||
var zeroVector = new Vector<byte>(0);
|
||||
|
||||
var baseOffset = 0;
|
||||
for (; baseOffset + Vector<byte>.Count <= length; baseOffset += Vector<byte>.Count)
|
||||
{
|
||||
if (new Vector<byte>(array, baseOffset + offset) != zeroVector)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var remaining = length - baseOffset;
|
||||
for (var i = 0; i < remaining; i++)
|
||||
{
|
||||
if (array[offset + baseOffset + i] != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a function that checks if the fingerprint at the given
|
||||
/// offset (arg 2) in the first argument (arg 1) is equal to the
|
||||
/// fingerprint in arg 3.
|
||||
/// </summary>
|
||||
/// <param name="fingerprintSize">Number of bytes in the fingerprint</param>
|
||||
/// <param name="entriesPerBucket">Number of entries in each bucket</param>
|
||||
/// <returns>The created delegate</returns>
|
||||
public static Func<byte[], int, byte[], int> CreateFingerprintComparator(uint fingerprintSize,
|
||||
uint entriesPerBucket)
|
||||
{
|
||||
int GetFingerprintIndex(byte[] originalFingerprintBytes,
|
||||
int originalFingerprintOffset,
|
||||
byte[] newFingerprintBytes)
|
||||
{
|
||||
var size = (int)fingerprintSize;
|
||||
var span = originalFingerprintBytes.AsSpan()[originalFingerprintOffset..];
|
||||
for (var entryIndex = 0; entryIndex < entriesPerBucket; entryIndex++)
|
||||
{
|
||||
var fingerprintBytes = span.Slice(size * entryIndex, size);
|
||||
if (fingerprintBytes.SequenceEqual(newFingerprintBytes))
|
||||
{
|
||||
return entryIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return GetFingerprintIndex;
|
||||
//var e1 = Emit<Func<byte[], int, byte[], int>>.NewDynamicMethod();
|
||||
|
||||
//Label nextStatement = null;
|
||||
//for (var entryIndex = 0; entryIndex < entriesPerBucket; entryIndex++)
|
||||
//{
|
||||
// if (nextStatement != null)
|
||||
// {
|
||||
// e1.MarkLabel(nextStatement);
|
||||
// }
|
||||
|
||||
// nextStatement = e1.DefineLabel();
|
||||
// for (var byteIndex = 0; byteIndex < fingerprintSize; byteIndex++)
|
||||
// {
|
||||
// // Retrieve a[offset + i]
|
||||
// e1.LoadArgument(0);
|
||||
// e1.LoadArgument(1);
|
||||
|
||||
// var offset = (int)(fingerprintSize * entryIndex + byteIndex);
|
||||
// if (offset > 0)
|
||||
// {
|
||||
// e1.LoadConstant(offset);
|
||||
// e1.Add();
|
||||
// }
|
||||
// e1.LoadElement<byte>();
|
||||
|
||||
// // Retrieve b[i]
|
||||
// e1.LoadArgument(2);
|
||||
// e1.LoadConstant(byteIndex);
|
||||
// e1.LoadElement<byte>();
|
||||
|
||||
// // Go to the false return of not equal
|
||||
// e1.UnsignedBranchIfNotEqual(nextStatement);
|
||||
// }
|
||||
|
||||
// e1.LoadConstant(entryIndex);
|
||||
// e1.Return();
|
||||
//}
|
||||
|
||||
//e1.MarkLabel(nextStatement);
|
||||
//e1.LoadConstant(-1);
|
||||
//e1.Return();
|
||||
|
||||
//return e1.CreateDelegate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a function that checks if the fingerprint at the given
|
||||
/// offset (arg 2) in the first argument (arg 1) is zero.
|
||||
/// </summary>
|
||||
/// <param name="fingerprintSize">Number of bytes in the fingerprint</param>
|
||||
/// <returns>The created delegate</returns>
|
||||
public static Func<byte[], int, bool> CreateZeroChecker(uint fingerprintSize)
|
||||
{
|
||||
Func<byte[], int, bool> func = (fingerprintBytes,
|
||||
offset) =>
|
||||
{
|
||||
var bytes = fingerprintBytes.AsSpan().Slice(offset, (int)fingerprintSize);
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
if (b != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return func;
|
||||
|
||||
//var e1 = Emit<Func<byte[], int, bool>>.NewDynamicMethod();
|
||||
//var returnFalse = e1.DefineLabel();
|
||||
|
||||
//for (var byteIndex = 0; byteIndex < fingerprintSize; byteIndex++)
|
||||
//{
|
||||
// // Load the contents array:
|
||||
// e1.LoadArgument(0);
|
||||
|
||||
// // Set the index we want to offset + i:
|
||||
// e1.LoadArgument(1);
|
||||
// if (byteIndex > 0)
|
||||
// {
|
||||
// e1.LoadConstant(byteIndex);
|
||||
// e1.Add();
|
||||
// }
|
||||
// e1.LoadElement<byte>();
|
||||
|
||||
|
||||
// // Go to the next statement (the next entry check) if it's not 0.
|
||||
// e1.LoadConstant(0);
|
||||
// e1.UnsignedBranchIfNotEqual(returnFalse);
|
||||
//}
|
||||
|
||||
//// Got down here? We're good, true true.
|
||||
//e1.LoadConstant(true);
|
||||
//e1.Return();
|
||||
|
||||
//// False branch:
|
||||
//e1.MarkLabel(returnFalse);
|
||||
//e1.LoadConstant(false);
|
||||
//e1.Return();
|
||||
|
||||
//return e1.CreateDelegate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a function that checks inserts a fingerprint (arg 3) into
|
||||
/// the index (arg 2) in the contents (arg 1) if there's any available
|
||||
/// unassigned fingerprint slot. Returns true if the insertion was
|
||||
/// made successfully.
|
||||
/// </summary>
|
||||
/// <param name="fingerprintSize">Number of bytes in the fingerprint</param>
|
||||
/// <param name="entriesPerBucket">Number of entries in each bucket</param>
|
||||
/// <returns>The created delegate</returns>
|
||||
public static Func<byte[], int, byte[], bool> CreateInsertIntoBucket(uint fingerprintSize, uint entriesPerBucket)
|
||||
{
|
||||
Func<byte[], int, byte[], bool> func = (fingerprintBytes,
|
||||
fingerprintIndex,
|
||||
newFingerprintBytes) =>
|
||||
{
|
||||
var zeroBytes = new byte[fingerprintSize];
|
||||
var bytes = fingerprintBytes.AsSpan().Slice(fingerprintIndex);
|
||||
var size = (int)fingerprintSize;
|
||||
for (var i = 0; i < bytes.Length; i += size)
|
||||
{
|
||||
var span = bytes.Slice(i, size);
|
||||
if (span.SequenceEqual(zeroBytes))
|
||||
{
|
||||
newFingerprintBytes.CopyTo(span);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
return func;
|
||||
|
||||
//var e = Emit<Func<byte[], int, byte[], bool>>.NewDynamicMethod();
|
||||
|
||||
//Label nextStatement = null;
|
||||
//for (var entryIndex = 0; entryIndex < entriesPerBucket; entryIndex++)
|
||||
//{
|
||||
// if (nextStatement != null)
|
||||
// {
|
||||
// e.MarkLabel(nextStatement);
|
||||
// }
|
||||
|
||||
// nextStatement = e.DefineLabel();
|
||||
|
||||
// // First, check that all bytes in the target spot are 0.
|
||||
// for (var byteIndex = 0; byteIndex < fingerprintSize; byteIndex++)
|
||||
// {
|
||||
// // Load the contents array:
|
||||
// e.LoadArgument(0);
|
||||
|
||||
// // Set the index we want to offset + i:
|
||||
// e.LoadArgument(1);
|
||||
// var offset = (int)(fingerprintSize * entryIndex + byteIndex);
|
||||
// if (offset > 0)
|
||||
// {
|
||||
// e.LoadConstant(offset);
|
||||
// e.Add();
|
||||
// }
|
||||
|
||||
// // Load the element at contents[offset]:
|
||||
// e.LoadElement<byte>();
|
||||
|
||||
// // Go to the next statement (the next entry check) if it's not 0.
|
||||
// e.LoadConstant(0);
|
||||
// e.UnsignedBranchIfNotEqual(nextStatement);
|
||||
// }
|
||||
|
||||
// // If we're here, we found a free space that we can put our
|
||||
// // fingerprint in.
|
||||
// for (var byteIndex = 0; byteIndex < fingerprintSize; byteIndex++)
|
||||
// {
|
||||
// // Element setting takes three values: the array to load
|
||||
// // into, the offset, and the data to load.
|
||||
|
||||
// // Array to load into:
|
||||
// e.LoadArgument(0);
|
||||
|
||||
// // Target offset in the array:
|
||||
// e.LoadArgument(1);
|
||||
// var offset = (int)(fingerprintSize * entryIndex + byteIndex);
|
||||
// if (offset > 0)
|
||||
// {
|
||||
// e.LoadConstant(offset);
|
||||
// e.Add();
|
||||
// }
|
||||
|
||||
// // Load the value to be loaded from the fingerprint:
|
||||
// e.LoadArgument(2);
|
||||
// e.LoadConstant(byteIndex);
|
||||
// e.LoadElement<byte>();
|
||||
|
||||
// // Store it!
|
||||
// e.StoreElement<byte>();
|
||||
// }
|
||||
|
||||
// // Now we're good, retur:
|
||||
// e.LoadConstant(true);
|
||||
// e.Return();
|
||||
//}
|
||||
|
||||
//e.MarkLabel(nextStatement);
|
||||
//e.LoadConstant(false);
|
||||
//e.Return();
|
||||
|
||||
//return e.CreateDelegate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CuckooFilter.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
[assembly: InternalsVisibleTo("Cuckoo.UnitTests")]
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the Cuckoo filter.
|
||||
/// </summary>
|
||||
public class CuckooFilter : ICuckooFilter, IEquatable<CuckooFilter>
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of bytes in a <see cref="uint" />
|
||||
/// </summary>
|
||||
private const int Uint32Bytes = sizeof(uint);
|
||||
|
||||
/// <summary>
|
||||
/// Comparator function, see <see cref="CodegenHelpers.CreateFingerprintComparator" />
|
||||
/// </summary>
|
||||
private readonly Func<byte[], int, byte[], int> _comparator;
|
||||
|
||||
/// <summary>
|
||||
/// Hashing algorithm.
|
||||
/// </summary>
|
||||
private readonly IHashAlgorithm _hashAlgorithm;
|
||||
|
||||
/// <summary>
|
||||
/// Comparator function, see <see cref="CodegenHelpers.CreateInsertIntoBucket" />
|
||||
/// </summary>
|
||||
private readonly Func<byte[], int, byte[], bool> _insertIntoBucket;
|
||||
|
||||
/// <summary>
|
||||
/// Pre-allocated buffer we hash value indices into.
|
||||
/// </summary>
|
||||
private readonly byte[] _valuesBuffer = new byte[Uint32Bytes];
|
||||
|
||||
/// <summary>
|
||||
/// Comparator function, see <see cref="CodegenHelpers.CreateZeroChecker" />
|
||||
/// </summary>
|
||||
private readonly Func<byte[], int, bool> _zeroCheck;
|
||||
|
||||
/// <summary>
|
||||
/// Random instance.
|
||||
/// </summary>
|
||||
internal readonly Random Random;
|
||||
|
||||
/// <summary>
|
||||
/// Pre-allocated buffer we hash the fingerprint into.
|
||||
/// </summary>
|
||||
private byte[] _fingerprintBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Pre-allocated buffer we use for swapping fingerprints into.
|
||||
/// </summary>
|
||||
private byte[] _fingerprintSwapBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CuckooFilter.
|
||||
/// </summary>
|
||||
/// <param name="buckets">Number of Buckets to store</param>
|
||||
/// <param name="entriesPerBucket">
|
||||
/// Number of fingerprints stored
|
||||
/// in each bucket.
|
||||
/// </param>
|
||||
/// <param name="fingerprintLength">Length of the fingerprint to use</param>
|
||||
/// <param name="maxKicks">
|
||||
/// Maximum number of times to relocate a value
|
||||
/// on a collision.
|
||||
/// </param>
|
||||
/// <param name="hashAlgorithm">Hashing algorithm to use</param>
|
||||
/// <param name="randomSeed">Random seed value</param>
|
||||
public CuckooFilter(
|
||||
uint buckets,
|
||||
uint entriesPerBucket,
|
||||
uint fingerprintLength,
|
||||
uint? maxKicks = null,
|
||||
IHashAlgorithm hashAlgorithm = null,
|
||||
int? randomSeed = null)
|
||||
{
|
||||
Buckets = buckets;
|
||||
EntriesPerBucket = entriesPerBucket;
|
||||
_hashAlgorithm = hashAlgorithm ?? XxHashAlgorithm.Instance;
|
||||
MaxKicks = maxKicks ?? buckets;
|
||||
|
||||
if (UpperPower2(buckets) != buckets)
|
||||
{
|
||||
throw new ArgumentException("Buckets must be a power of 2", nameof(buckets));
|
||||
}
|
||||
|
||||
_fingerprintBuffer = new byte[fingerprintLength];
|
||||
Contents = CreateEmptyBucketData(buckets, entriesPerBucket, fingerprintLength);
|
||||
Random = randomSeed == null
|
||||
? new Random()
|
||||
: new Random(randomSeed.Value);
|
||||
BytesPerBucket = EntriesPerBucket * fingerprintLength;
|
||||
_fingerprintSwapBuffer = new byte[_fingerprintBuffer.Length];
|
||||
_comparator = CodegenHelpers.CreateFingerprintComparator(fingerprintLength, entriesPerBucket);
|
||||
_zeroCheck = CodegenHelpers.CreateZeroChecker(fingerprintLength);
|
||||
_insertIntoBucket = CodegenHelpers.CreateInsertIntoBucket(fingerprintLength, entriesPerBucket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new optimally-sized CuckooFilter with a target
|
||||
/// false-positive-at-capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">Filter capacity</param>
|
||||
/// <param name="falsePositiveRate">Desired false positive rate.</param>
|
||||
/// <param name="hashAlgorithm">Hashing algorithm to use</param>
|
||||
/// <param name="randomSeed">Random seed value</param>
|
||||
public CuckooFilter(
|
||||
uint capacity,
|
||||
double falsePositiveRate,
|
||||
IHashAlgorithm hashAlgorithm = null,
|
||||
int? randomSeed = null)
|
||||
{
|
||||
_hashAlgorithm = hashAlgorithm ?? XxHashAlgorithm.Instance;
|
||||
|
||||
// "In summary, we choose (2, 4)-cuckoo filter (i.e., each item has
|
||||
// two candidate Buckets and each bucket has up to four fingerprints)
|
||||
// as the default configuration, because it achieves the best or
|
||||
// close - to - best space efficiency for the false positive
|
||||
// rates that most practical applications""
|
||||
EntriesPerBucket = 4;
|
||||
|
||||
// Equation here from page 8, step 6, of the paper:
|
||||
// ceil(log_2 (2b / \epsilon)
|
||||
var desiredLength = Math.Log(2 * (float)EntriesPerBucket / falsePositiveRate, 2);
|
||||
var fingerprintLength = (uint)Math.Ceiling(desiredLength / 8);
|
||||
_fingerprintBuffer = new byte[fingerprintLength];
|
||||
|
||||
// Not explicitly defined in the paper, however this is the
|
||||
// algorithm used in the author's implementation:
|
||||
// https://github.com/efficient/cuckoofilter/blob/master/src/cuckoofilter.h#L89
|
||||
Buckets = UpperPower2(capacity / EntriesPerBucket);
|
||||
if ((double)capacity / Buckets / EntriesPerBucket > 0.96)
|
||||
{
|
||||
Buckets <<= 1;
|
||||
}
|
||||
|
||||
MaxKicks = Buckets;
|
||||
Random = randomSeed == null
|
||||
? new Random()
|
||||
: new Random(randomSeed.Value);
|
||||
Contents = CreateEmptyBucketData(Buckets, EntriesPerBucket, fingerprintLength);
|
||||
BytesPerBucket = EntriesPerBucket * fingerprintLength;
|
||||
_fingerprintSwapBuffer = new byte[_fingerprintBuffer.Length];
|
||||
_zeroCheck = CodegenHelpers.CreateZeroChecker(fingerprintLength);
|
||||
_comparator = CodegenHelpers.CreateFingerprintComparator(fingerprintLength, 4);
|
||||
_insertIntoBucket = CodegenHelpers.CreateInsertIntoBucket(fingerprintLength, EntriesPerBucket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CuckooFilter.
|
||||
/// </summary>
|
||||
/// <param name="contents">Contents of th efilter</param>
|
||||
/// <param name="entriesPerBucket">
|
||||
/// Number of fingerprints stored
|
||||
/// in each bucket.
|
||||
/// </param>
|
||||
/// <param name="fingerprintLength">Length of the fingerprint to use</param>
|
||||
/// <param name="maxKicks">
|
||||
/// Maximum number of times to relocate a value
|
||||
/// on a collision.
|
||||
/// </param>
|
||||
/// <param name="hashAlgorithm">Hashing algorithm to use</param>
|
||||
internal CuckooFilter(
|
||||
byte[] contents,
|
||||
uint entriesPerBucket,
|
||||
uint fingerprintLength,
|
||||
uint maxKicks,
|
||||
IHashAlgorithm hashAlgorithm = null)
|
||||
{
|
||||
Contents = contents;
|
||||
MaxKicks = maxKicks;
|
||||
EntriesPerBucket = entriesPerBucket;
|
||||
BytesPerBucket = entriesPerBucket * fingerprintLength;
|
||||
Buckets = (uint)contents.Length / BytesPerBucket;
|
||||
_fingerprintBuffer = new byte[fingerprintLength];
|
||||
_fingerprintSwapBuffer = new byte[_fingerprintBuffer.Length];
|
||||
_hashAlgorithm = hashAlgorithm ?? XxHashAlgorithm.Instance;
|
||||
_zeroCheck = CodegenHelpers.CreateZeroChecker(fingerprintLength);
|
||||
_comparator = CodegenHelpers.CreateFingerprintComparator(fingerprintLength, 4);
|
||||
_insertIntoBucket = CodegenHelpers.CreateInsertIntoBucket(fingerprintLength, entriesPerBucket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Buckets the filter contains.
|
||||
/// </summary>
|
||||
public uint Buckets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes each bucket takes.
|
||||
/// </summary>
|
||||
public uint BytesPerBucket { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of fingerprints to store per bucket.
|
||||
/// </summary>
|
||||
public uint EntriesPerBucket { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the fingerprint.
|
||||
/// </summary>
|
||||
public uint FingerprintLength => (uint)_fingerprintBuffer.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max number of times we'll try to kick and item from a
|
||||
/// bucket when we insert before giving it.
|
||||
/// </summary>
|
||||
public uint MaxKicks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the cuckoo filter.
|
||||
/// </summary>
|
||||
internal byte[] Contents { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size, in memory, of the filter.
|
||||
/// </summary>
|
||||
public uint Size => (uint)Contents.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the filter probably contains
|
||||
/// the given item.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to check</param>
|
||||
/// <returns>True if the filter contains the value, false otherwise.</returns>
|
||||
public bool Contains(byte[] value)
|
||||
{
|
||||
var fingerprint = GetFingerprint(value);
|
||||
_hashAlgorithm.Hash(_valuesBuffer, value, Uint32Bytes);
|
||||
|
||||
var index1 = ToInt32(_valuesBuffer) & (Buckets - 1);
|
||||
if (_comparator(Contents, IndexToOffset(index1), fingerprint) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var index2 = DeriveIndex2(fingerprint, index1);
|
||||
if (_comparator(Contents, IndexToOffset(index2), fingerprint) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the value into the filter. Whereas <see cref="TryInsert" />
|
||||
/// returns false if the value cannot be inserted, this throws.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <exception cref="FilterFullException">
|
||||
/// Thrown if the filter is
|
||||
/// too full to accept the value.
|
||||
/// </exception>
|
||||
public void Insert(byte[] value)
|
||||
{
|
||||
if (!TryInsert(value))
|
||||
{
|
||||
throw new FilterFullException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the filter.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to remove</param>
|
||||
/// <returns>True if the filter contained the value, false otherwise.</returns>
|
||||
public bool Remove(byte[] value)
|
||||
{
|
||||
var fingerprint = GetFingerprint(value);
|
||||
_hashAlgorithm.Hash(_valuesBuffer, value, Uint32Bytes);
|
||||
|
||||
var index1 = ToInt32(_valuesBuffer) & (Buckets - 1);
|
||||
|
||||
var offset = IndexToOffset(index1);
|
||||
var removal = _comparator(Contents, offset, fingerprint);
|
||||
if (removal != -1)
|
||||
{
|
||||
Array.Clear(Contents, offset + _fingerprintBuffer.Length * removal, fingerprint.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
var index2 = DeriveIndex2(fingerprint, index1);
|
||||
offset = IndexToOffset(index2);
|
||||
removal = _comparator(Contents, offset, fingerprint);
|
||||
if (removal != -1)
|
||||
{
|
||||
Array.Clear(Contents, offset + _fingerprintBuffer.Length * removal, fingerprint.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the value into the filter.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <returns>
|
||||
/// True if it was inserted successfully, false if the
|
||||
/// filter was too full to do so.
|
||||
/// </returns>
|
||||
public bool TryInsert(byte[] value)
|
||||
{
|
||||
return TryInsertInner(value);
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
/// <returns>
|
||||
/// true if the current object is equal to the <paramref name="other">other</paramref> parameter; otherwise,
|
||||
/// false.
|
||||
/// </returns>
|
||||
public bool Equals(CuckooFilter other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Buckets == other.Buckets
|
||||
&& BytesPerBucket == other.BytesPerBucket
|
||||
&& CodegenHelpers.BytesEquals(Contents, other.Contents)
|
||||
&& EntriesPerBucket == other.EntriesPerBucket
|
||||
&& MaxKicks == other.MaxKicks;
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified object is equal to the current object.</summary>
|
||||
/// <param name="obj">The object to compare with the current object.</param>
|
||||
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((CuckooFilter)obj);
|
||||
}
|
||||
|
||||
/// <summary>Serves as the default hash function.</summary>
|
||||
/// <returns>A hash code for the current object.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var contentHash = new byte[4];
|
||||
_hashAlgorithm.Hash(contentHash, Contents, 4);
|
||||
|
||||
var hashCode = (int)Buckets;
|
||||
hashCode = (hashCode * 397) ^ (int)BytesPerBucket;
|
||||
hashCode = (hashCode * 397) ^ (contentHash[0] | (contentHash[1] << 8)
|
||||
| (contentHash[2] << 16)
|
||||
| (contentHash[3] << 24));
|
||||
hashCode = (hashCode * 397) ^ (int)EntriesPerBucket;
|
||||
hashCode = (hashCode * 397) ^ (int)MaxKicks;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the value into the filter.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <returns>
|
||||
/// True if it was inserted successfully, false if the
|
||||
/// filter was too full to do so.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryInsertInner(byte[] value)
|
||||
{
|
||||
var fingerprint = GetFingerprint(value);
|
||||
_hashAlgorithm.Hash(_valuesBuffer, value, Uint32Bytes);
|
||||
var index1 = BoundToBucketCount(ToInt32(_valuesBuffer));
|
||||
|
||||
if (_insertIntoBucket(Contents, IndexToOffset(index1), fingerprint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var index2 = DeriveIndex2(fingerprint, index1);
|
||||
if (_insertIntoBucket(Contents, IndexToOffset(index2), fingerprint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetIndex = Random.Next(1) == 0
|
||||
? index1
|
||||
: index2;
|
||||
|
||||
for (var i = 0; i < MaxKicks; i++)
|
||||
{
|
||||
fingerprint = SwapIntoBucket(fingerprint, targetIndex);
|
||||
targetIndex = DeriveIndex2(fingerprint, targetIndex);
|
||||
|
||||
if (_insertIntoBucket(Contents, IndexToOffset(targetIndex), fingerprint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted version of the filter. Used for
|
||||
/// examining internal state while testing.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IList<IList<string>> DumpDebug()
|
||||
{
|
||||
var list = new List<IList<string>>();
|
||||
for (var offset = 0L; offset < Contents.Length; offset += BytesPerBucket)
|
||||
{
|
||||
var items = new List<string>();
|
||||
for (var i = 0; i < EntriesPerBucket; i++)
|
||||
{
|
||||
var target = (int)offset + i * _fingerprintBuffer.Length;
|
||||
if (!_zeroCheck(Contents, target))
|
||||
{
|
||||
items.Add(Encoding.ASCII.GetString(Contents, target, _fingerprintBuffer.Length));
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(items);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static byte[] CreateEmptyBucketData(long buckets, uint itemsPerBucket, uint bytesPerItem)
|
||||
{
|
||||
return new byte[buckets * itemsPerBucket * bytesPerItem];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ToInt32(byte[] data)
|
||||
{
|
||||
var x = data[0] << 24;
|
||||
x |= data[1] << 16;
|
||||
x |= data[2] << 8;
|
||||
x |= data[3];
|
||||
return x;
|
||||
}
|
||||
|
||||
private static uint UpperPower2(uint x)
|
||||
{
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x |= x >> 32;
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the value is less than or equal to the number of Buckets.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to bound</param>
|
||||
/// <returns>Truncated value</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private long BoundToBucketCount(long value)
|
||||
{
|
||||
// this.Buckets is always a power of 2, so to ensure index1 is <=1
|
||||
// the number of Buckets, we mask it against Buckets - 1. So if
|
||||
// Buckets is 16 (0b10000), we mask it against (0b01111).
|
||||
return value & (Buckets - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the alternative index for an item, given its primary
|
||||
/// index and fingerprint.
|
||||
/// </summary>
|
||||
/// <param name="fingerprint">Fingerprint value</param>
|
||||
/// <param name="index1">Primary index</param>
|
||||
/// <returns>The secondary index</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private long DeriveIndex2(byte[] fingerprint, long index1)
|
||||
{
|
||||
_hashAlgorithm.Hash(_valuesBuffer, fingerprint, Uint32Bytes);
|
||||
return index1 ^ BoundToBucketCount(ToInt32(_valuesBuffer));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private byte[] GetFingerprint(byte[] input)
|
||||
{
|
||||
_hashAlgorithm.Hash(_fingerprintBuffer, input, _fingerprintBuffer.Length);
|
||||
if (_zeroCheck(_fingerprintBuffer, 0))
|
||||
{
|
||||
for (var i = 0; i < _fingerprintBuffer.Length; i++)
|
||||
{
|
||||
_fingerprintBuffer[i] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return _fingerprintBuffer;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int IndexToOffset(long index)
|
||||
{
|
||||
return (int)(index * BytesPerBucket);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private byte[] SwapIntoBucket(byte[] fingerprint, long bucket)
|
||||
{
|
||||
var subIndex = Random.Next((int)EntriesPerBucket);
|
||||
var offset = IndexToOffset(bucket) + subIndex * fingerprint.Length;
|
||||
var newFingerprint = _fingerprintSwapBuffer;
|
||||
|
||||
Array.Copy(Contents, offset, _fingerprintSwapBuffer, 0, fingerprint.Length);
|
||||
Array.Copy(fingerprint, 0, Contents, offset, fingerprint.Length);
|
||||
_fingerprintSwapBuffer = fingerprint;
|
||||
_fingerprintBuffer = newFingerprint;
|
||||
|
||||
return newFingerprint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CuckooFilterExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for the Cuckoo filter.
|
||||
/// </summary>
|
||||
public static class CuckooFilterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the filter probably contains
|
||||
/// the given item.
|
||||
/// </summary>
|
||||
/// <param name="filter">Cuckoo filter instance.</param>
|
||||
/// <param name="value">Value to check</param>
|
||||
/// <returns>True if the filter contains the value, false otherwise.</returns>
|
||||
public static bool ContainsString(this CuckooFilter filter, string value)
|
||||
{
|
||||
return filter.Contains(Encoding.ASCII.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the value into the filter. Whereas <see cref="TryInsertString" />
|
||||
/// returns false if the value cannot be inserted, this throws.
|
||||
/// </summary>
|
||||
/// <param name="filter">Cuckoo filter instance.</param>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <exception cref="FilterFullException">
|
||||
/// Thrown if the filter is
|
||||
/// too full to accept the value.
|
||||
/// </exception>
|
||||
public static bool InsertString(this CuckooFilter filter, string value)
|
||||
{
|
||||
return filter.TryInsert(Encoding.ASCII.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the value into the filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">Cuckoo filter instance.</param>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <returns>
|
||||
/// True if it was inserted successfully, false if the
|
||||
/// filter was too full to do so.
|
||||
/// </returns>
|
||||
public static bool TryInsertString(this CuckooFilter filter, string value)
|
||||
{
|
||||
return filter.TryInsert(Encoding.ASCII.GetBytes(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="FilterFullException.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown in <see cref="CuckooFilter.Insert" /> if the filter is
|
||||
/// too full to accept more items.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class FilterFullException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the exception.
|
||||
/// </summary>
|
||||
public FilterFullException()
|
||||
: base("The cuckoo filter is too full to accept more items")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ConfigureAwait />
|
||||
</Weavers>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,50 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="GzipStreamSerializer.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Serializer that creates a gzipped stream.
|
||||
/// </summary>
|
||||
public class GzipStreamSerializer : SimpleStreamSerializer
|
||||
{
|
||||
private readonly CompressionLevel _compressionLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new serialize that serializes into a gzipped string.
|
||||
/// </summary>
|
||||
/// <param name="compressionLevel">Gzip compression level</param>
|
||||
public GzipStreamSerializer(CompressionLevel compressionLevel = CompressionLevel.Optimal)
|
||||
{
|
||||
_compressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the CuckooFilter to from a stream.
|
||||
/// </summary>
|
||||
/// <param name="source">Source stream to read</param>
|
||||
/// <param name="hashAlgorithm">Hash algorithm</param>
|
||||
public override CuckooFilter Deserialize(Stream source, IHashAlgorithm hashAlgorithm = null)
|
||||
{
|
||||
var compressionStream = new GZipStream(source, CompressionMode.Decompress);
|
||||
return base.Deserialize(compressionStream, hashAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the CuckooFilter to a stream.
|
||||
/// </summary>
|
||||
/// <param name="target">Target stream to write to</param>
|
||||
/// <param name="filter">Filter to serialize</param>
|
||||
public override void Serialize(Stream target, CuckooFilter filter)
|
||||
{
|
||||
var compressionStream = new GZipStream(target, _compressionLevel);
|
||||
base.Serialize(compressionStream, filter);
|
||||
compressionStream.Flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ICuckooFilter.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Cuckoo filter interface.
|
||||
/// </summary>
|
||||
public interface ICuckooFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the total size, in memory, of the filter.
|
||||
/// </summary>
|
||||
uint Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the filter probably contains
|
||||
/// the given item.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to check</param>
|
||||
/// <returns>True if the filter contains the value, false otherwise.</returns>
|
||||
bool Contains(byte[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the value into the filter. Whereas <see cref="CuckooFilter.TryInsert" />
|
||||
/// returns false if the value cannot be inserted, this throws.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <exception cref="FilterFullException">
|
||||
/// Thrown if the filter is
|
||||
/// too full to accept the value.
|
||||
/// </exception>
|
||||
void Insert(byte[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the filter.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to remove</param>
|
||||
/// <returns>True if the filter contained the value, false otherwise.</returns>
|
||||
bool Remove(byte[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the value into the filter.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <returns>
|
||||
/// True if it was inserted successfully, false if the
|
||||
/// filter was too full to do so.
|
||||
/// </returns>
|
||||
bool TryInsert(byte[] value);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ICuckooSerializer.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
public interface ICuckooSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes the CuckooFilter to from a stream.
|
||||
/// </summary>
|
||||
/// <param name="source">Source stream to read</param>
|
||||
/// <param name="hashAlgorithm">Hash algorithm</param>
|
||||
CuckooFilter Deserialize(Stream source, IHashAlgorithm hashAlgorithm = null);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the CuckooFilter to a stream.
|
||||
/// </summary>
|
||||
/// <param name="target">Target stream to write to</param>
|
||||
/// <param name="filter">Filter to serialize</param>
|
||||
void Serialize(Stream target, CuckooFilter filter);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IHashAlgorithm.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a type used for hashing a value for the cuckoo filter.
|
||||
/// </summary>
|
||||
public interface IHashAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a hash of the value.
|
||||
/// </summary>
|
||||
/// <param name="target">Target array to hash to</param>
|
||||
/// <param name="value">Value to hash</param>
|
||||
/// <param name="hashLength">Number of bytes to write into the target.</param>
|
||||
void Hash(byte[] target, byte[] value, int hashLength);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Microsoft.Cuckoo</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>Microsoft</Authors>
|
||||
<Company>Microsoft Corporation</Company>
|
||||
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
|
||||
<RepositoryType>GitHub</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/microsoft/mixer-cuckoo</RepositoryUrl>
|
||||
<ProjectUrl>https://github.com/microsoft/mixer-cuckoo</ProjectUrl>
|
||||
<Description>A cuckoo filter implementation for C#</Description>
|
||||
<License>MIT</License>
|
||||
<RequireLicenseAcceptance>true</RequireLicenseAcceptance>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Sigil" />
|
||||
<PackageReference Include="Standart.Hash.xxHash" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Fody">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,80 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="SimpleStreamSerializer.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// Simple stream serializer that writes the cuckoo filter directly out
|
||||
/// to a stream. Investigate <see cref="GzipStreamSerializer" /> for a more
|
||||
/// storage-friendly version.
|
||||
/// </summary>
|
||||
public class SimpleStreamSerializer : ICuckooSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes the CuckooFilter to from a stream.
|
||||
/// </summary>
|
||||
/// <param name="source">Source stream to read</param>
|
||||
/// <param name="hashAlgorithm">Hash algorithm</param>
|
||||
public virtual CuckooFilter Deserialize(Stream source, IHashAlgorithm hashAlgorithm = null)
|
||||
{
|
||||
var paramsReadBuffer = new byte[16];
|
||||
//source.Read(paramsReadBuffer, 0, 16);
|
||||
source.ReadExactly(paramsReadBuffer);
|
||||
|
||||
var entriesPerBucket = ReadBigEndianUint(paramsReadBuffer, 0);
|
||||
var fingerprintLength = ReadBigEndianUint(paramsReadBuffer, 4);
|
||||
var buckets = ReadBigEndianUint(paramsReadBuffer, 8);
|
||||
var maxKicks = ReadBigEndianUint(paramsReadBuffer, 12);
|
||||
|
||||
var contents = new byte[buckets * entriesPerBucket * fingerprintLength];
|
||||
//source.Read(contents, 0, contents.Length);
|
||||
source.ReadExactly(contents);
|
||||
|
||||
return new CuckooFilter(
|
||||
contents,
|
||||
entriesPerBucket,
|
||||
fingerprintLength,
|
||||
maxKicks,
|
||||
hashAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the CuckooFilter to a stream.
|
||||
/// </summary>
|
||||
/// <param name="target">Target stream to write to</param>
|
||||
/// <param name="filter">Filter to serialize</param>
|
||||
public virtual void Serialize(Stream target, CuckooFilter filter)
|
||||
{
|
||||
var writeBuffer = new byte[16];
|
||||
WriteBigEndianUint(writeBuffer, filter.EntriesPerBucket, 0);
|
||||
WriteBigEndianUint(writeBuffer, filter.FingerprintLength, 4);
|
||||
WriteBigEndianUint(writeBuffer, filter.Buckets, 8);
|
||||
WriteBigEndianUint(writeBuffer, filter.MaxKicks, 12);
|
||||
target.Write(writeBuffer, 0, 16);
|
||||
target.Write(filter.Contents, 0, filter.Contents.Length);
|
||||
}
|
||||
|
||||
private static uint ReadBigEndianUint(byte[] target, int offset)
|
||||
{
|
||||
uint x = 0;
|
||||
x |= (uint)(target[offset] << 24);
|
||||
x |= (uint)(target[offset + 1] << 16);
|
||||
x |= (uint)(target[offset + 2] << 8);
|
||||
x |= target[offset + 3];
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
private static void WriteBigEndianUint(byte[] target, uint number, int offset)
|
||||
{
|
||||
target[offset] = (byte)(number >> 24);
|
||||
target[offset + 1] = (byte)(number >> 16);
|
||||
target[offset + 2] = (byte)(number >> 8);
|
||||
target[offset + 3] = (byte)number;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="XxHashAlgorithm.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Standart.Hash.xxHash;
|
||||
|
||||
namespace Microsoft.Cuckoo;
|
||||
|
||||
/// <summary>
|
||||
/// An IHashAlgorithm using XxHash.
|
||||
/// </summary>
|
||||
public class XxHashAlgorithm : IHashAlgorithm
|
||||
{
|
||||
internal static readonly IHashAlgorithm Instance = new XxHashAlgorithm();
|
||||
|
||||
private readonly ulong _seed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new xxhash algorithm instance.
|
||||
/// </summary>
|
||||
/// <param name="seed">Seed value for the hash</param>
|
||||
public XxHashAlgorithm(ulong seed = 0)
|
||||
{
|
||||
_seed = seed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash of the value.
|
||||
/// </summary>
|
||||
/// <param name="target">Target array to hash to</param>
|
||||
/// <param name="value">Value to hash</param>
|
||||
/// <param name="hashLength">Desired length of the fingerprint.</param>
|
||||
public void Hash(byte[] target, byte[] value, int hashLength)
|
||||
{
|
||||
var hash = xxHash64.ComputeHash(value, value.Length, _seed);
|
||||
for (var i = 0; i < hashLength; i++)
|
||||
{
|
||||
target[i] = (byte)(hash & 0xFF);
|
||||
hash >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public enum ConnectionType
|
||||
{
|
||||
UnKnown = 0,
|
||||
Generic = 1,
|
||||
Media = 2,
|
||||
Push = 4
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="AuthKeyId"></param>
|
||||
/// <param name="MsgKey">int128,length=16</param>
|
||||
/// <param name="EncryptedData"></param>
|
||||
/// <param name="ConnectionId"></param>
|
||||
/// <param name="ConnectionType"></param>
|
||||
/// <param name="ClientIp"></param>
|
||||
/// <param name="RequestId"></param>
|
||||
/// <param name="Date"></param>
|
||||
public record EncryptedMessage(long AuthKeyId,
|
||||
ReadOnlyMemory<byte> MsgKey,
|
||||
ReadOnlyMemory<byte> EncryptedData,
|
||||
string ConnectionId,
|
||||
ConnectionType ConnectionType,
|
||||
string ClientIp,
|
||||
Guid RequestId,
|
||||
long Date
|
||||
) : IMtpMessage
|
||||
{
|
||||
public string ConnectionId { get; set; } = ConnectionId;
|
||||
public ConnectionType ConnectionType { get; set; } = ConnectionType;
|
||||
public string ClientIp { get; set; } = ClientIp;
|
||||
[JsonIgnore]public IMemoryOwner<byte>? MemoryOwner { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public record EncryptedMessageResponse(long AuthKeyId,
|
||||
ReadOnlyMemory<byte> Data,
|
||||
string ConnectionId,
|
||||
long SeqNumber
|
||||
) : IMayHaveMemoryOwner
|
||||
{
|
||||
public IMemoryOwner<byte>? MemoryOwner { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ConfigureAwait />
|
||||
</Weavers>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,2 @@
|
||||
global using System.Buffers;
|
||||
global using System.Text.Json.Serialization;
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IDataProcessor<in TData>
|
||||
{
|
||||
Task ProcessAsync(TData data);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IEventDataSerializer<TEventData> : IEventDataSerializer
|
||||
{
|
||||
void Serialize(IBufferWriter<byte> writer, TEventData data);
|
||||
TEventData Deserialize(ReadOnlyMemory<byte> buffer);
|
||||
}
|
||||
|
||||
public interface IEventDataSerializer
|
||||
{
|
||||
//object? Deserialize(Type type, ReadOnlyMemory<byte> buffer);
|
||||
object Deserialize(Type type, ReadOnlyMemory<byte> buffer);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IMayHaveMemoryOwner
|
||||
{
|
||||
IMemoryOwner<byte>? MemoryOwner { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IMessageIdHelper
|
||||
{
|
||||
long GenerateMessageId();
|
||||
|
||||
long GenerateUniqueId();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IMessageQueueProcessor<in TData>
|
||||
{
|
||||
void Enqueue(TData message,
|
||||
long key);
|
||||
|
||||
Task ProcessAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface IMtpMessage : IMayHaveMemoryOwner
|
||||
{
|
||||
string ClientIp { get; set; }
|
||||
string ConnectionId { get; set; }
|
||||
ConnectionType ConnectionType { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface ISingletonDependency { }
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
|
||||
public interface ITransientDependency { }
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MyTelegram.Abstractions;
|
||||
public static class MyTelegramAbstractionsExtensions
|
||||
{
|
||||
public static void RegisterServices(this IServiceCollection services, Assembly? assembly = null)
|
||||
{
|
||||
assembly ??= Assembly.GetCallingAssembly();
|
||||
|
||||
var singletonBaseInterface = typeof(ISingletonDependency);
|
||||
var transientBaseInterface = typeof(ITransientDependency);
|
||||
|
||||
var types = assembly.GetTypes()
|
||||
.Where(p => p != singletonBaseInterface && p != transientBaseInterface && !p.IsAbstract)
|
||||
.ToList()
|
||||
;
|
||||
var singletonTypes = types.Where(singletonBaseInterface.IsAssignableFrom);
|
||||
var transientTypes = types.Where(transientBaseInterface.IsAssignableFrom);
|
||||
|
||||
foreach (var type in singletonTypes)
|
||||
{
|
||||
var baseInterfaces = type.GetInterfaces();
|
||||
foreach (var baseInterface in baseInterfaces)
|
||||
{
|
||||
services.AddSingleton(baseInterface, type);
|
||||
}
|
||||
|
||||
services.AddSingleton(type);
|
||||
}
|
||||
|
||||
foreach (var type in transientTypes)
|
||||
{
|
||||
var baseInterfaces = type.GetInterfaces();
|
||||
foreach (var baseInterface in baseInterfaces)
|
||||
{
|
||||
services.AddTransient(baseInterface, type);
|
||||
}
|
||||
|
||||
services.AddTransient(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
public record UnencryptedMessage(
|
||||
long AuthKeyId,
|
||||
string ClientIp,
|
||||
string ConnectionId,
|
||||
ConnectionType ConnectionType,
|
||||
ReadOnlyMemory<byte> MessageData,
|
||||
int MessageDataLength,
|
||||
long MessageId,
|
||||
uint ObjectId,
|
||||
Guid RequestId,
|
||||
long Date
|
||||
) : IMtpMessage
|
||||
{
|
||||
public string ConnectionId { get; set; } = ConnectionId;
|
||||
public ConnectionType ConnectionType { get; set; } = ConnectionType;
|
||||
public string ClientIp { get; set; } = ClientIp;
|
||||
public IMemoryOwner<byte>? MemoryOwner { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MyTelegram.Abstractions;
|
||||
public record UnencryptedMessageResponse(long AuthKeyId,
|
||||
ReadOnlyMemory<byte> Data,
|
||||
string ConnectionId,
|
||||
long ReqMsgId) : IMayHaveMemoryOwner
|
||||
{
|
||||
public IMemoryOwner<byte>? MemoryOwner { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MyTelegram.Messenger.Services.Interfaces;
|
||||
|
||||
namespace MyTelegram.Admin.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/service-notifications")]
|
||||
public class ServiceNotificationsController : ControllerBase
|
||||
{
|
||||
private readonly IServiceNotificationAppService _serviceNotificationAppService;
|
||||
private readonly ILogger<ServiceNotificationsController> _logger;
|
||||
|
||||
public ServiceNotificationsController(
|
||||
IServiceNotificationAppService serviceNotificationAppService,
|
||||
ILogger<ServiceNotificationsController> logger)
|
||||
{
|
||||
_serviceNotificationAppService = serviceNotificationAppService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost("send")]
|
||||
public async Task<IActionResult> SendNotification([FromBody] SendNotificationRequest request)
|
||||
{
|
||||
_logger.LogInformation("Received notification request for user {UserId}: Type={Type}, Popup={Popup}",
|
||||
request.UserId, request.Type, request.Popup);
|
||||
|
||||
try
|
||||
{
|
||||
await _serviceNotificationAppService.SendServiceNotificationAsync(
|
||||
request.UserId,
|
||||
request.Type,
|
||||
request.Message,
|
||||
request.Popup,
|
||||
media: null, // TODO: добавить поддержку медиа
|
||||
entities: null,
|
||||
invertMedia: false
|
||||
);
|
||||
|
||||
_logger.LogInformation("Notification sent successfully to user {UserId}", request.UserId);
|
||||
|
||||
return Ok(new { success = true, message = "Notification sent" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send notification to user {UserId}", request.UserId);
|
||||
return StatusCode(500, new { success = false, error = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SendNotificationRequest
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public bool Popup { get; set; }
|
||||
public string? MediaUrl { get; set; }
|
||||
public string? MediaType { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
|
||||
COPY ["Directory.Build.props", "."]
|
||||
COPY ["Directory.Packages.props", "."]
|
||||
COPY ["src/MyTelegram.Admin.Api/MyTelegram.Admin.Api.csproj", "src/MyTelegram.Admin.Api/"]
|
||||
COPY ["src/MyTelegram.Domain/MyTelegram.Domain.csproj", "src/MyTelegram.Domain/"]
|
||||
COPY ["src/MyTelegram.Domain.Shared/MyTelegram.Domain.Shared.csproj", "src/MyTelegram.Domain.Shared/"]
|
||||
COPY ["src/MyTelegram.Services/MyTelegram.Services.csproj", "src/MyTelegram.Services/"]
|
||||
COPY ["src/MyTelegram.Queries/MyTelegram.Queries.csproj", "src/MyTelegram.Queries/"]
|
||||
COPY ["src/MyTelegram.ReadModel.Interfaces/MyTelegram.ReadModel.Interfaces.csproj", "src/MyTelegram.ReadModel.Interfaces/"]
|
||||
COPY ["src/MyTelegram.ReadModel/MyTelegram.ReadModel.csproj", "src/MyTelegram.ReadModel/"]
|
||||
COPY ["src/MyTelegram.Schema/MyTelegram.Schema.csproj", "src/MyTelegram.Schema/"]
|
||||
COPY ["src/MyTelegram.Core/MyTelegram.Core.csproj", "src/MyTelegram.Core/"]
|
||||
COPY ["src/MyTelegram.EventFlow/MyTelegram.EventFlow.csproj", "src/MyTelegram.EventFlow/"]
|
||||
COPY ["src/MyTelegram.EventFlow.MongoDB/MyTelegram.EventFlow.MongoDB.csproj", "src/MyTelegram.EventFlow.MongoDB/"]
|
||||
COPY ["src/MyTelegram.EventBus/MyTelegram.EventBus.csproj", "src/MyTelegram.EventBus/"]
|
||||
COPY ["src/MyTelegram.EventBus.RabbitMQ/MyTelegram.EventBus.RabbitMQ.csproj", "src/MyTelegram.EventBus.RabbitMQ/"]
|
||||
COPY ["src/MyTelegram.Abstractions/MyTelegram.Abstractions.csproj", "src/MyTelegram.Abstractions/"]
|
||||
|
||||
RUN dotnet restore "./src/MyTelegram.Admin.Api/MyTelegram.Admin.Api.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/src/MyTelegram.Admin.Api"
|
||||
RUN dotnet build "./MyTelegram.Admin.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./MyTelegram.Admin.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "MyTelegram.Admin.Api.dll"]
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace MyTelegram.Admin.Api.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware аутентификации для Admin API.
|
||||
/// Для всех админских эндпоинтов требует API-ключ или токен.
|
||||
/// </summary>
|
||||
public class AdminAuthMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<AdminAuthMiddleware> _logger;
|
||||
private readonly string _adminApiKey;
|
||||
|
||||
public AdminAuthMiddleware(RequestDelegate next, ILogger<AdminAuthMiddleware> logger, IConfiguration configuration)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
|
||||
// Берём ключ из конфигурации или переменной окружения,
|
||||
// значение по умолчанию пригодно только для разработки
|
||||
_adminApiKey = configuration["ADMIN_API_KEY"]
|
||||
?? Environment.GetEnvironmentVariable("ADMIN_API_KEY")
|
||||
?? "CHANGE_ME_IN_PRODUCTION";
|
||||
|
||||
if (_adminApiKey == "CHANGE_ME_IN_PRODUCTION")
|
||||
{
|
||||
_logger.LogWarning("Using default Admin API key! Set ADMIN_API_KEY environment variable.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Admin API authentication enabled with custom key");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
var path = context.Request.Path.Value;
|
||||
|
||||
// Проверяем аутентификацию для всех эндпоинтов /api/*
|
||||
if (path != null && path.StartsWith("/api/"))
|
||||
{
|
||||
// Сначала ищем ключ в заголовке X-Admin-API-Key
|
||||
if (!context.Request.Headers.TryGetValue("X-Admin-API-Key", out var apiKey) ||
|
||||
apiKey != _adminApiKey)
|
||||
{
|
||||
// Затем пробуем заголовок Authorization с Bearer-токеном
|
||||
if (!context.Request.Headers.TryGetValue("Authorization", out var authHeader) ||
|
||||
!authHeader.ToString().StartsWith("Bearer ") ||
|
||||
authHeader.ToString().Substring(7) != _adminApiKey)
|
||||
{
|
||||
_logger.LogWarning("Unauthorized Admin API access attempt - IP: {IP}, Path: {Path}",
|
||||
context.Connection.RemoteIpAddress, path);
|
||||
|
||||
context.Response.StatusCode = 401;
|
||||
context.Response.ContentType = "application/json";
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
success = false,
|
||||
error = "Unauthorized: Invalid or missing API key"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogDebug("Admin API access authorized - Path: {Path}", path);
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="MessagePack" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MyTelegram.Domain\MyTelegram.Domain.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.Domain.Shared\MyTelegram.Domain.Shared.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.Services\MyTelegram.Services.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.Queries\MyTelegram.Queries.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.EventFlow.MongoDB\MyTelegram.EventFlow.MongoDB.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.EventBus.RabbitMQ\MyTelegram.EventBus.RabbitMQ.csproj" />
|
||||
<ProjectReference Include="..\MyTelegram.Messenger\MyTelegram.Messenger.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,231 @@
|
||||
using EventFlow;
|
||||
using EventFlow.Extensions;
|
||||
using EventFlow.MongoDB.Extensions;
|
||||
using MyTelegram;
|
||||
using MyTelegram.Domain.Commands.User;
|
||||
using MyTelegram.Domain.Aggregates.User;
|
||||
using MyTelegram.Domain.Shared.Events;
|
||||
using MyTelegram.EventBus;
|
||||
using MyTelegram.Abstractions;
|
||||
using MyTelegram.Admin.Api.Serializers;
|
||||
using MyTelegram.EventBus.RabbitMQ.Extensions;
|
||||
using MyTelegram.EventBus.RabbitMQ;
|
||||
using MyTelegram.Messenger.Extensions;
|
||||
using MyTelegram.ReadModel.MongoDB;
|
||||
using MyTelegram.ReadModel.Impl;
|
||||
using MyTelegram.Schema;
|
||||
using MongoDB.Driver;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Слушаем порт 5000
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.ListenAnyIP(5000);
|
||||
});
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
// Настройки RabbitMQ и MongoDB
|
||||
builder.Services.Configure<EventBusRabbitMqOptions>(builder.Configuration.GetSection("RabbitMQ:EventBus"));
|
||||
builder.Services.Configure<RabbitMqOptions>(builder.Configuration.GetSection("RabbitMQ:Connections:Default"));
|
||||
|
||||
var connectionString = builder.Configuration.GetConnectionString("Default") ?? "mongodb://mongodb:27017";
|
||||
var databaseName = builder.Configuration.GetValue<string>("App:DatabaseName") ?? "eventflow";
|
||||
|
||||
var mongoClient = new MongoClient(connectionString);
|
||||
builder.Services.AddSingleton<IMongoClient>(mongoClient);
|
||||
builder.Services.AddSingleton<IMongoDatabase>(_ => mongoClient.GetDatabase(databaseName));
|
||||
|
||||
// Подключаем EventFlow с хранилищем событий в MongoDB
|
||||
builder.Services.AddEventFlow(options =>
|
||||
{
|
||||
options.AddDefaults(typeof(UserAggregate).Assembly);
|
||||
options.UseMongoDbEventStore();
|
||||
options.ConfigureMongoDb(connectionString, databaseName);
|
||||
});
|
||||
|
||||
// Регистрируем только нужные сериализаторы, не подключая весь набор сервисов
|
||||
// мессенджера, иначе сериализаторы Mongo конфликтуют между собой
|
||||
try
|
||||
{
|
||||
builder.Services.RegisterMongoDbSerializer();
|
||||
}
|
||||
catch (MongoDB.Bson.BsonSerializationException)
|
||||
{
|
||||
// Сериализатор мог быть уже зарегистрирован, повторную регистрацию пропускаем
|
||||
}
|
||||
|
||||
// Шина событий поверх RabbitMQ
|
||||
builder.Services.AddTransient<IEventDataSerializer<BotGiftEvent>, BotGiftEventSerializer>();
|
||||
builder.Services.AddMyTelegramRabbitMqEventBus();
|
||||
|
||||
// Регистрируем сервис отправки служебных уведомлений вручную
|
||||
builder.Services.AddScoped<MyTelegram.Messenger.Services.Interfaces.IServiceNotificationAppService,
|
||||
MyTelegram.Messenger.Services.Impl.ServiceNotificationAppService>();
|
||||
|
||||
// CORS для админ-панели
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAdmin", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseCors("AllowAdmin");
|
||||
|
||||
// Ограничиваем размер тела запроса, чтобы не переполнять память
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
const long maxSize = 10 * 1024 * 1024; // 10MB
|
||||
var feature = context.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature>();
|
||||
if (feature != null && !feature.IsReadOnly)
|
||||
{
|
||||
feature.MaxRequestBodySize = maxSize;
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
app.UseMiddleware<MyTelegram.Admin.Api.Middleware.AdminAuthMiddleware>();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.MapPost("/api/admin/star-gifts/send", async (
|
||||
AdminSendStarGiftRequest request,
|
||||
IMongoDatabase mongoDatabase,
|
||||
IEventBus eventBus) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request.Count <= 0)
|
||||
{
|
||||
return Results.BadRequest(new { success = false, error = "Count must be positive" });
|
||||
}
|
||||
|
||||
var giftId = request.GiftId;
|
||||
var giftCollection = mongoDatabase.GetCollection<AvailableStarGiftReadModel>("AvailableStarGiftReadModel");
|
||||
var gift = await giftCollection.Find(x => x.GiftId == giftId).FirstOrDefaultAsync();
|
||||
if (gift == null)
|
||||
{
|
||||
return Results.NotFound(new { success = false, error = "Gift not found" });
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var sent = 0;
|
||||
var canUpgrade = request.CanUpgrade ?? (gift.UpgradeStars.HasValue);
|
||||
|
||||
for (var i = 0; i < request.Count; i++)
|
||||
{
|
||||
var evt = new BotGiftEvent
|
||||
{
|
||||
BotUserId = request.FromUserId,
|
||||
ToUserId = request.ToUserId,
|
||||
GiftId = gift.GiftId,
|
||||
Count = 1,
|
||||
Message = request.Message,
|
||||
HideName = request.NameHidden,
|
||||
IncludeUpgrade = canUpgrade,
|
||||
Timestamp = now,
|
||||
RandomId = Random.Shared.NextInt64()
|
||||
};
|
||||
|
||||
await eventBus.PublishAsync(evt);
|
||||
sent++;
|
||||
}
|
||||
|
||||
return Results.Ok(new { success = true, sent });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Results.BadRequest(new { success = false, error = ex.Message });
|
||||
}
|
||||
});
|
||||
|
||||
// Установка верификации пользователя
|
||||
app.MapPost("/api/admin/verification/users/{userId}", async (
|
||||
long userId,
|
||||
SetUserVerificationRequest request,
|
||||
ICommandBus commandBus) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new SetCustomVerificationCommand(
|
||||
UserId.Create(userId),
|
||||
RequestInfo.Empty with { Date = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() },
|
||||
true,
|
||||
request.BotUserId,
|
||||
userId,
|
||||
request.IconEmojiId,
|
||||
request.Description,
|
||||
request.CustomDescription
|
||||
);
|
||||
|
||||
await commandBus.PublishAsync(command, CancellationToken.None);
|
||||
|
||||
return Results.Ok(new { success = true, message = "Verification set successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Results.BadRequest(new { success = false, error = ex.Message });
|
||||
}
|
||||
});
|
||||
|
||||
// Снятие верификации пользователя
|
||||
app.MapDelete("/api/admin/verification/users/{userId}", async (
|
||||
long userId,
|
||||
ICommandBus commandBus) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new SetCustomVerificationCommand(
|
||||
UserId.Create(userId),
|
||||
RequestInfo.Empty with { Date = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() },
|
||||
false,
|
||||
null,
|
||||
userId,
|
||||
0,
|
||||
string.Empty,
|
||||
null
|
||||
);
|
||||
|
||||
await commandBus.PublishAsync(command, CancellationToken.None);
|
||||
|
||||
return Results.Ok(new { success = true, message = "Verification removed successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Results.BadRequest(new { success = false, error = ex.Message });
|
||||
}
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
public record AdminSendStarGiftRequest(
|
||||
long FromUserId,
|
||||
long ToUserId,
|
||||
long GiftId,
|
||||
int Count,
|
||||
bool NameHidden,
|
||||
bool? CanUpgrade,
|
||||
string? Message
|
||||
);
|
||||
|
||||
public record SetUserVerificationRequest(
|
||||
long BotUserId,
|
||||
long IconEmojiId,
|
||||
string Description,
|
||||
string? CustomDescription
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Buffers;
|
||||
using MessagePack;
|
||||
using MyTelegram.Abstractions;
|
||||
using MyTelegram.Domain.Shared.Events;
|
||||
|
||||
namespace MyTelegram.Admin.Api.Serializers;
|
||||
|
||||
public class BotGiftEventSerializer : IEventDataSerializer<BotGiftEvent>
|
||||
{
|
||||
private static readonly MessagePackSerializerOptions Options = MessagePackSerializerOptions.Standard
|
||||
.WithResolver(MessagePack.Resolvers.ContractlessStandardResolver.Instance);
|
||||
|
||||
public object Deserialize(Type type, ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
return MessagePackSerializer.Deserialize<BotGiftEvent>(buffer, Options)!;
|
||||
}
|
||||
|
||||
public BotGiftEvent Deserialize(ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
return MessagePackSerializer.Deserialize<BotGiftEvent>(buffer, Options);
|
||||
}
|
||||
|
||||
public void Serialize(IBufferWriter<byte> writer, BotGiftEvent value)
|
||||
{
|
||||
MessagePackSerializer.Serialize(writer, value, Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"Default": "mongodb://user:CHANGE_ME@mongodb:27017"
|
||||
},
|
||||
"App": {
|
||||
"DatabaseName": "eventflow",
|
||||
"ReadModelDatabaseName": "readmodel"
|
||||
},
|
||||
"RabbitMQ": {
|
||||
"Connections": {
|
||||
"Default": {
|
||||
"HostName": "rabbitmq",
|
||||
"Port": 5672,
|
||||
"UserName": "guest",
|
||||
"Password": "guest"
|
||||
}
|
||||
},
|
||||
"EventBus": {
|
||||
"ClientName": "MyTelegram.Admin.Api",
|
||||
"ExchangeName": "MyTelegram"
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
namespace MyTelegram.AuthServer.BackgroundServices;
|
||||
|
||||
public class MyTelegramAuthServerBackgroundService(
|
||||
ILogger<MyTelegramAuthServerBackgroundService> logger,
|
||||
IHandlerHelper handlerHelper,
|
||||
IFingerprintHelper fingerprintHelper
|
||||
) : BackgroundService
|
||||
{
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
handlerHelper.InitAllHandlers();
|
||||
logger.LogInformation("MyTelegram auth server started");
|
||||
const long defaultFingerprint = -3591632762792723036;
|
||||
var fingerprint = fingerprintHelper.GetFingerprint();
|
||||
if (fingerprint == defaultFingerprint)
|
||||
{
|
||||
logger.LogWarning("You are currently using the default private key, which anyone can obtain from the mytelegram open source project. For security reasons, please use your own private key and replace the client's public key.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
|
||||
USER $APP_UID
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
# This stage is used to build the service project
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["Directory.Packages.props", "."]
|
||||
COPY ["Directory.Build.props", "."]
|
||||
COPY ["nuget.config", "."]
|
||||
COPY ["src/MyTelegram.AuthServer/MyTelegram.AuthServer.csproj", "src/MyTelegram.AuthServer/"]
|
||||
COPY ["src/MyTelegram.Caching.Redis/MyTelegram.Caching.Redis.csproj", "src/MyTelegram.Caching.Redis/"]
|
||||
COPY ["src/MyTelegram.Core/MyTelegram.Core.csproj", "src/MyTelegram.Core/"]
|
||||
COPY ["src/MyTelegram.Abstractions/MyTelegram.Abstractions.csproj", "src/MyTelegram.Abstractions/"]
|
||||
COPY ["src/MyTelegram.Domain.Shared/MyTelegram.Domain.Shared.csproj", "src/MyTelegram.Domain.Shared/"]
|
||||
COPY ["src/MyTelegram.Schema/MyTelegram.Schema.csproj", "src/MyTelegram.Schema/"]
|
||||
COPY ["src/MyTelegram.EventBus.Rebus/MyTelegram.EventBus.Rebus.csproj", "src/MyTelegram.EventBus.Rebus/"]
|
||||
COPY ["src/MyTelegram.EventBus/MyTelegram.EventBus.csproj", "src/MyTelegram.EventBus/"]
|
||||
COPY ["src/MyTelegram.Services/MyTelegram.Services.csproj", "src/MyTelegram.Services/"]
|
||||
COPY ["src/MyTelegram.EventFlow/MyTelegram.EventFlow.csproj", "src/MyTelegram.EventFlow/"]
|
||||
RUN dotnet restore "./src/MyTelegram.AuthServer/MyTelegram.AuthServer.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/src/MyTelegram.AuthServer"
|
||||
RUN dotnet build "./MyTelegram.AuthServer.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
# This stage is used to publish the service project to be copied to the final stage
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./MyTelegram.AuthServer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "MyTelegram.AuthServer.dll"]
|
||||
@@ -0,0 +1,92 @@
|
||||
namespace MyTelegram.AuthServer.EventHandlers;
|
||||
|
||||
public class UnencryptedMessageHandler(
|
||||
ILogger<UnencryptedMessageHandler> logger,
|
||||
IHandlerHelper handlerHelper,
|
||||
IEventBus eventBus
|
||||
) : IEventHandler<UnencryptedMessage>, ITransientDependency
|
||||
{
|
||||
public async Task HandleEventAsync(UnencryptedMessage eventData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!handlerHelper.TryGetHandler(eventData.ObjectId, out var handler))
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Cannot find a handler with objectId {ObjectId:x2}, connectionId: {ConnectionId}",
|
||||
eventData.ObjectId,
|
||||
eventData.ConnectionId
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
logger.LogTrace(
|
||||
"Processing unencrypted message, connectionId: {ConnectionId}, [objectId]: {ObjectId:x2}, handler: {Handler}, reqMsgId: {ReqMsgId}",
|
||||
eventData.ConnectionId,
|
||||
eventData.ObjectId,
|
||||
handler.GetType().Name,
|
||||
eventData.MessageId
|
||||
);
|
||||
}
|
||||
|
||||
var obj = eventData.MessageData.ToTObject<IObject>();
|
||||
|
||||
var r = await handler.HandleAsync(
|
||||
new RequestInput(
|
||||
eventData.ConnectionId,
|
||||
eventData.RequestId,
|
||||
eventData.ObjectId,
|
||||
eventData.MessageId,
|
||||
0,
|
||||
0,
|
||||
eventData.AuthKeyId,
|
||||
eventData.AuthKeyId,
|
||||
0,
|
||||
eventData.Date,
|
||||
DeviceType.Unknown,
|
||||
eventData.ClientIp,
|
||||
0,
|
||||
0
|
||||
),
|
||||
obj
|
||||
);
|
||||
|
||||
if (r != null!)
|
||||
{
|
||||
using var writer = new ArrayPoolBufferWriter<byte>();
|
||||
r.Serialize(writer);
|
||||
var unencryptedResponse = new UnencryptedMessageResponse(
|
||||
eventData.AuthKeyId,
|
||||
writer.WrittenMemory,
|
||||
eventData.ConnectionId,
|
||||
eventData.MessageId
|
||||
);
|
||||
await eventBus.PublishAsync(unencryptedResponse);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(
|
||||
"Process request failed, connectionId: {ConnectionId}, reqMsgId: {ReqMsgId}, error: {Error}",
|
||||
eventData.ConnectionId,
|
||||
eventData.MessageId,
|
||||
ex
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
eventData.MemoryOwner?.Dispose();
|
||||
}
|
||||
|
||||
if (logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
logger.LogTrace(
|
||||
"Process unencrypted message completed, reqMsgId: {ReqMsgId}",
|
||||
eventData.MessageId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user