Compare commits

...

4 commits

Author SHA1 Message Date
Aleksey Obukhov 47da331806 Адаптация к tdlib версии 1.8.14 2023-07-29 22:11:36 -04:00
Aleksandr Zelenin 063c3471b0 readme fix 2023-03-31 05:56:57 +03:00
Aleksandr Zelenin ff46ac12bc readme fix 2023-03-24 05:32:34 +03:00
Aleksandr Zelenin c54167dc40 add example 2023-03-24 05:29:41 +03:00
13 changed files with 30613 additions and 4976 deletions

View file

@ -1,4 +1,4 @@
TAG := v1.8.0
TAG := v1.8.14
schema-update:
curl https://raw.githubusercontent.com/tdlib/td/${TAG}/td/generate/scheme/td_api.tl 2>/dev/null > ./data/td_api.tl

View file

@ -1,11 +1,13 @@
# go-tdlib
Go wrapper for [TDLib (Telegram Database Library)](https://github.com/tdlib/td) with full support of TDLib v1.8.0
Go wrapper for [TDLib (Telegram Database Library)](https://github.com/tdlib/td) with full support of TDLib v1.8.14
## TDLib installation
Use [TDLib build instructions](https://tdlib.github.io/td/build.html) with checkmarked `Install built TDLib to /usr/local instead of placing the files to td/tdlib`.
### Note: Compatible with TDLib v1.8.14 only!
### Windows
Build with environment variables:
@ -135,6 +137,17 @@ tdlibClient, err := client.NewClient(authorizer, proxy)
```
## Example
[Example application](https://github.com/zelenin/go-tdlib/tree/master/example)
```
cd example
docker build --network host --build-arg TD_TAG=v1.8.14 --tag tdlib-test .
docker run --rm -it -e "API_ID=00000" -e "API_HASH=abcdef0123456789" tdlib-test ash
./app
```
## Notes
* WIP. Library API can be changed in the future

View file

@ -8,6 +8,42 @@ import (
var ErrNotSupportedAuthorizationState = errors.New("not supported state")
// Contains parameters for TDLib initialization
type TdlibParameters struct {
// Pass true to use Telegram test environment instead of the production environment
UseTestDc bool `json:"use_test_dc"`
// The path to the directory for the persistent database; if empty, the current working directory will be used
DatabaseDirectory string `json:"database_directory"`
// The path to the directory for storing files; if empty, database_directory will be used
FilesDirectory string `json:"files_directory"`
// Encryption key for the database. If the encryption key is invalid, then an error with code 401 will be returned
DatabaseEncryptionKey []byte `json:"database_encryption_key"`
// Pass true to keep information about downloaded and uploaded files between application restarts
UseFileDatabase bool `json:"use_file_database"`
// Pass true to keep cache of users, basic groups, supergroups, channels and secret chats between restarts. Implies use_file_database
UseChatInfoDatabase bool `json:"use_chat_info_database"`
// Pass true to keep cache of chats and messages between restarts. Implies use_chat_info_database
UseMessageDatabase bool `json:"use_message_database"`
// Pass true to enable support for secret chats
UseSecretChats bool `json:"use_secret_chats"`
// Application identifier for Telegram API access, which can be obtained at https://my.telegram.org
ApiId int32 `json:"api_id"`
// Application identifier hash for Telegram API access, which can be obtained at https://my.telegram.org
ApiHash string `json:"api_hash"`
// IETF language tag of the user's operating system language; must be non-empty
SystemLanguageCode string `json:"system_language_code"`
// Model of the device the application is being run on; must be non-empty
DeviceModel string `json:"device_model"`
// Version of the operating system the application is being run on. If empty, the version is automatically detected by TDLib
SystemVersion string `json:"system_version"`
// Application version; must be non-empty
ApplicationVersion string `json:"application_version"`
// Pass true to automatically delete old files in background
EnableStorageOptimizer bool `json:"enable_storage_optimizer"`
// Pass true to ignore original file names for downloaded files. Otherwise, downloaded files are saved under names as close as possible to the original name
IgnoreFileNames bool `json:"ignore_file_names"`
}
type AuthorizationStateHandler interface {
Handle(client *Client, state AuthorizationState) error
Close()
@ -65,15 +101,27 @@ func (stateHandler *clientAuthorizer) Handle(client *Client, state Authorization
switch state.AuthorizationStateType() {
case TypeAuthorizationStateWaitTdlibParameters:
p := <-stateHandler.TdlibParameters
_, err := client.SetTdlibParameters(&SetTdlibParametersRequest{
Parameters: <-stateHandler.TdlibParameters,
UseTestDc: p.UseTestDc,
DatabaseDirectory: p.DatabaseDirectory,
FilesDirectory: p.FilesDirectory,
DatabaseEncryptionKey: p.DatabaseEncryptionKey,
UseFileDatabase: p.UseFileDatabase,
UseChatInfoDatabase: p.UseChatInfoDatabase,
UseMessageDatabase: p.UseMessageDatabase,
UseSecretChats: p.UseSecretChats,
ApiId: p.ApiId,
ApiHash: p.ApiHash,
SystemLanguageCode: p.SystemLanguageCode,
DeviceModel: p.DeviceModel,
SystemVersion: p.SystemVersion,
ApplicationVersion: p.ApplicationVersion,
EnableStorageOptimizer: p.EnableStorageOptimizer,
IgnoreFileNames: p.IgnoreFileNames,
})
return err
case TypeAuthorizationStateWaitEncryptionKey:
_, err := client.CheckDatabaseEncryptionKey(&CheckDatabaseEncryptionKeyRequest{})
return err
case TypeAuthorizationStateWaitPhoneNumber:
_, err := client.SetAuthenticationPhoneNumber(&SetAuthenticationPhoneNumberRequest{
PhoneNumber: <-stateHandler.PhoneNumber,
@ -85,12 +133,21 @@ func (stateHandler *clientAuthorizer) Handle(client *Client, state Authorization
})
return err
case TypeAuthorizationStateWaitEmailAddress:
return ErrNotSupportedAuthorizationState
case TypeAuthorizationStateWaitEmailCode:
return ErrNotSupportedAuthorizationState
case TypeAuthorizationStateWaitCode:
_, err := client.CheckAuthenticationCode(&CheckAuthenticationCodeRequest{
Code: <-stateHandler.Code,
})
return err
case TypeAuthorizationStateWaitOtherDeviceConfirmation:
return ErrNotSupportedAuthorizationState
case TypeAuthorizationStateWaitRegistration:
return ErrNotSupportedAuthorizationState
@ -140,6 +197,12 @@ func CliInteractor(clientAuthorizer *clientAuthorizer) {
clientAuthorizer.PhoneNumber <- phoneNumber
case TypeAuthorizationStateWaitEmailAddress:
return
case TypeAuthorizationStateWaitEmailCode:
return
case TypeAuthorizationStateWaitCode:
var code string
@ -148,6 +211,12 @@ func CliInteractor(clientAuthorizer *clientAuthorizer) {
clientAuthorizer.Code <- code
case TypeAuthorizationStateWaitOtherDeviceConfirmation:
return
case TypeAuthorizationStateWaitRegistration:
return
case TypeAuthorizationStateWaitPassword:
fmt.Println("Enter password: ")
var password string
@ -185,15 +254,27 @@ func (stateHandler *botAuthorizer) Handle(client *Client, state AuthorizationSta
switch state.AuthorizationStateType() {
case TypeAuthorizationStateWaitTdlibParameters:
p := <-stateHandler.TdlibParameters
_, err := client.SetTdlibParameters(&SetTdlibParametersRequest{
Parameters: <-stateHandler.TdlibParameters,
UseTestDc: p.UseTestDc,
DatabaseDirectory: p.DatabaseDirectory,
FilesDirectory: p.FilesDirectory,
DatabaseEncryptionKey: p.DatabaseEncryptionKey,
UseFileDatabase: p.UseFileDatabase,
UseChatInfoDatabase: p.UseChatInfoDatabase,
UseMessageDatabase: p.UseMessageDatabase,
UseSecretChats: p.UseSecretChats,
ApiId: p.ApiId,
ApiHash: p.ApiHash,
SystemLanguageCode: p.SystemLanguageCode,
DeviceModel: p.DeviceModel,
SystemVersion: p.SystemVersion,
ApplicationVersion: p.ApplicationVersion,
EnableStorageOptimizer: p.EnableStorageOptimizer,
IgnoreFileNames: p.IgnoreFileNames,
})
return err
case TypeAuthorizationStateWaitEncryptionKey:
_, err := client.CheckDatabaseEncryptionKey(&CheckDatabaseEncryptionKeyRequest{})
return err
case TypeAuthorizationStateWaitPhoneNumber:
_, err := client.CheckAuthenticationBotToken(&CheckAuthenticationBotTokenRequest{
Token: <-stateHandler.Token,

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ package client
/*
#include <stdlib.h>
#include <string.h>
#include <td/telegram/td_json_client.h>
*/
import "C"
@ -83,7 +84,8 @@ func (instance *tdlib) receive(timeout time.Duration) (*Response, error) {
return nil, errors.New("update receiving timeout")
}
data := []byte(C.GoString(result))
resultLen := C.strlen(result)
data := C.GoBytes(unsafe.Pointer(result), C.int(resultLen))
var resp Response
@ -107,7 +109,8 @@ func Execute(req Request) (*Response, error) {
return nil, errors.New("request can't be parsed")
}
data = []byte(C.GoString(result))
resultLen := C.strlen(result)
data = C.GoBytes(unsafe.Pointer(result), C.int(resultLen))
var resp Response

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

85
example/Dockerfile Normal file
View file

@ -0,0 +1,85 @@
FROM alpine:3.17 as tdlib-builder
ENV LANG en_US.UTF-8
ENV TZ UTC
ARG TD_TAG
RUN apk update && \
apk upgrade && \
apk add --update \
build-base \
ca-certificates \
ccache \
cmake \
git \
gperf \
linux-headers \
openssl-dev \
php \
php-ctype \
readline-dev \
zlib-dev && \
git clone -b "${TD_TAG}" "https://github.com/tdlib/td.git" /src && \
mkdir /src/build && \
cd /src/build && \
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX:PATH=/usr/local \
.. && \
cmake --build . --target prepare_cross_compiling && \
cd .. && \
php SplitSource.php && \
cd build && \
cmake --build . --target install && \
ls -lah /usr/local
FROM golang:1.20.2-alpine3.17 as go-builder
ENV LANG en_US.UTF-8
ENV TZ UTC
RUN set -eux && \
apk update && \
apk upgrade && \
apk add \
bash \
build-base \
ca-certificates \
curl \
git \
linux-headers \
openssl-dev \
zlib-dev
WORKDIR /src
COPY --from=tdlib-builder /usr/local/include/td /usr/local/include/td/
COPY --from=tdlib-builder /usr/local/lib/libtd* /usr/local/lib/
COPY . /src
RUN go build \
-a \
-trimpath \
-ldflags "-s -w" \
-o app \
"./demo.go" && \
ls -lah
FROM alpine:3.17
ENV LANG en_US.UTF-8
ENV TZ UTC
RUN apk upgrade --no-cache && \
apk add --no-cache \
ca-certificates \
libstdc++
WORKDIR /app
COPY --from=go-builder /src/app .
CMD ["./app"]

83
example/demo.go Normal file
View file

@ -0,0 +1,83 @@
package main
import (
"log"
"os"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"github.com/zelenin/go-tdlib/client"
)
func main() {
authorizer := client.ClientAuthorizer()
go client.CliInteractor(authorizer)
var (
apiIdRaw = os.Getenv("API_ID")
apiHash = os.Getenv("API_HASH")
)
apiId64, err := strconv.ParseInt(apiIdRaw, 10, 32)
if err != nil {
log.Fatalf("strconv.Atoi error: %s", err)
}
apiId := int32(apiId64)
authorizer.TdlibParameters <- &client.TdlibParameters{
UseTestDc: false,
DatabaseDirectory: filepath.Join(".tdlib", "database"),
FilesDirectory: filepath.Join(".tdlib", "files"),
UseFileDatabase: true,
UseChatInfoDatabase: true,
UseMessageDatabase: true,
UseSecretChats: false,
ApiId: apiId,
ApiHash: apiHash,
SystemLanguageCode: "en",
DeviceModel: "Server",
SystemVersion: "1.0.0",
ApplicationVersion: "1.0.0",
EnableStorageOptimizer: true,
IgnoreFileNames: false,
}
_, err = client.SetLogVerbosityLevel(&client.SetLogVerbosityLevelRequest{
NewVerbosityLevel: 1,
})
if err != nil {
log.Fatalf("SetLogVerbosityLevel error: %s", err)
}
tdlibClient, err := client.NewClient(authorizer)
if err != nil {
log.Fatalf("NewClient error: %s", err)
}
optionValue, err := tdlibClient.GetOption(&client.GetOptionRequest{
Name: "version",
})
if err != nil {
log.Fatalf("GetOption error: %s", err)
}
log.Printf("TDLib version: %s", optionValue.(*client.OptionValueString).Value)
me, err := tdlibClient.GetMe()
if err != nil {
log.Fatalf("GetMe error: %s", err)
}
log.Printf("Me: %s %s [%s]", me.FirstName, me.LastName, me.Username)
ch := make(chan os.Signal, 2)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
go func() {
<-ch
tdlibClient.Stop()
os.Exit(1)
}()
}

5
example/go.mod Normal file
View file

@ -0,0 +1,5 @@
module go-tdlib-demo
go 1.20
require github.com/zelenin/go-tdlib v0.6.0 // indirect

2
example/go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/zelenin/go-tdlib v0.6.0 h1:dmdaPYcluNPiVuagM7D2FPT/nLYftTmqeKndz30vSDM=
github.com/zelenin/go-tdlib v0.6.0/go.mod h1:Xs8fXbk5n7VaPyrSs9DP7QYoBScWYsjX+lUcWmx1DIU=