go-tdlib/client/tdlib.go

222 lines
4.4 KiB
Go
Raw Permalink Normal View History

2018-08-30 14:55:42 +00:00
package client
2018-09-25 19:18:30 +00:00
/*
#include <stdlib.h>
#include <td/telegram/td_json_client.h>
*/
2018-08-30 14:55:42 +00:00
import "C"
import (
2018-10-23 12:38:10 +00:00
"encoding/json"
"errors"
"fmt"
2022-01-04 12:22:29 +00:00
"log"
2018-10-23 12:38:10 +00:00
"strconv"
2022-01-04 12:22:29 +00:00
"sync"
2018-10-23 12:38:10 +00:00
"time"
"unsafe"
2018-08-30 14:55:42 +00:00
)
2022-01-04 12:22:29 +00:00
var tdlibInstance *tdlib
func init() {
tdlibInstance = &tdlib{
timeout: 60 * time.Second,
clients: map[int]*Client{},
}
2018-08-30 14:55:42 +00:00
}
2022-01-04 12:22:29 +00:00
type tdlib struct {
once sync.Once
timeout time.Duration
mu sync.Mutex
clients map[int]*Client
}
func (instance *tdlib) addClient(client *Client) {
instance.mu.Lock()
defer instance.mu.Unlock()
instance.clients[client.jsonClient.id] = client
instance.once.Do(func() {
go instance.receiver()
})
}
func (instance *tdlib) getClient(id int) (*Client, error) {
instance.mu.Lock()
defer instance.mu.Unlock()
client, ok := instance.clients[id]
if !ok {
return nil, fmt.Errorf("client [id: %d] does not exist", id)
2018-10-23 12:38:10 +00:00
}
2022-01-04 12:22:29 +00:00
return client, nil
2018-08-30 14:55:42 +00:00
}
2022-01-04 12:22:29 +00:00
func (instance *tdlib) receiver() {
for {
resp, err := instance.receive(instance.timeout)
if err != nil {
continue
}
2018-08-30 14:55:42 +00:00
2022-01-04 12:22:29 +00:00
client, err := instance.getClient(resp.ClientId)
if err != nil {
log.Print(err)
continue
}
2018-08-30 14:55:42 +00:00
2022-01-04 12:22:29 +00:00
client.responses <- resp
}
2018-08-30 14:55:42 +00:00
}
// Receives incoming updates and request responses from the TDLib client. May be called from any thread, but
// shouldn't be called simultaneously from two different threads.
// Returned pointer will be deallocated by TDLib during next call to td_json_client_receive or td_json_client_execute
// in the same thread, so it can't be used after that.
2022-01-04 12:22:29 +00:00
func (instance *tdlib) receive(timeout time.Duration) (*Response, error) {
2021-04-05 03:04:54 +00:00
result := C.td_receive(C.double(float64(timeout) / float64(time.Second)))
2018-10-23 12:38:10 +00:00
if result == nil {
return nil, errors.New("update receiving timeout")
}
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
data := []byte(C.GoString(result))
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
var resp Response
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
err := json.Unmarshal(data, &resp)
if err != nil {
return nil, err
}
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
resp.Data = data
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
return &resp, nil
2018-08-30 14:55:42 +00:00
}
func Execute(req Request) (*Response, error) {
data, _ := json.Marshal(req)
query := C.CString(string(data))
defer C.free(unsafe.Pointer(query))
result := C.td_execute(query)
if result == nil {
return nil, errors.New("request can't be parsed")
}
data = []byte(C.GoString(result))
var resp Response
err := json.Unmarshal(data, &resp)
if err != nil {
return nil, err
}
resp.Data = data
return &resp, nil
}
2022-01-04 12:22:29 +00:00
type JsonClient struct {
id int
}
func NewJsonClient() *JsonClient {
return &JsonClient{
id: int(C.td_create_client_id()),
}
}
// Sends request to the TDLib client. May be called from any thread.
func (jsonClient *JsonClient) Send(req Request) {
data, _ := json.Marshal(req)
query := C.CString(string(data))
defer C.free(unsafe.Pointer(query))
C.td_send(C.int(jsonClient.id), query)
}
2018-08-30 14:55:42 +00:00
// Synchronously executes TDLib request. May be called from any thread.
// Only a few requests can be executed synchronously.
// Returned pointer will be deallocated by TDLib during next call to td_json_client_receive or td_json_client_execute
// in the same thread, so it can't be used after that.
func (jsonClient *JsonClient) Execute(req Request) (*Response, error) {
return Execute(req)
2018-08-30 14:55:42 +00:00
}
type meta struct {
2021-04-05 03:04:54 +00:00
Type string `json:"@type"`
Extra string `json:"@extra"`
ClientId int `json:"@client_id"`
2018-08-30 14:55:42 +00:00
}
type Request struct {
2018-10-23 12:38:10 +00:00
meta
Data map[string]interface{}
2018-08-30 14:55:42 +00:00
}
func (req Request) MarshalJSON() ([]byte, error) {
2018-10-23 12:38:10 +00:00
req.Data["@type"] = req.Type
req.Data["@extra"] = req.Extra
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
return json.Marshal(req.Data)
2018-08-30 14:55:42 +00:00
}
type Response struct {
2018-10-23 12:38:10 +00:00
meta
Data json.RawMessage
2018-08-30 14:55:42 +00:00
}
type ResponseError struct {
2018-10-23 12:38:10 +00:00
Err *Error
2018-08-30 14:55:42 +00:00
}
func (responseError ResponseError) Error() string {
2018-10-23 12:38:10 +00:00
return fmt.Sprintf("%d %s", responseError.Err.Code, responseError.Err.Message)
2018-08-30 14:55:42 +00:00
}
func buildResponseError(data json.RawMessage) error {
2018-10-23 12:38:10 +00:00
respErr, err := UnmarshalError(data)
if err != nil {
return err
}
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
return ResponseError{
Err: respErr,
}
2018-08-30 14:55:42 +00:00
}
// JsonInt64 alias for int64, in order to deal with json big number problem
type JsonInt64 int64
// MarshalJSON marshals to json
func (jsonInt64 JsonInt64) MarshalJSON() ([]byte, error) {
2019-11-06 17:23:19 +00:00
return []byte(`"` + strconv.FormatInt(int64(jsonInt64), 10) + `"`), nil
2018-08-30 14:55:42 +00:00
}
// UnmarshalJSON unmarshals from json
func (jsonInt64 *JsonInt64) UnmarshalJSON(data []byte) error {
2020-11-30 09:33:47 +00:00
if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
data = data[1 : len(data)-1]
}
jsonBigInt, err := strconv.ParseInt(string(data), 10, 64)
2018-10-23 12:38:10 +00:00
if err != nil {
return err
}
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
*jsonInt64 = JsonInt64(jsonBigInt)
2018-08-30 14:55:42 +00:00
2018-10-23 12:38:10 +00:00
return nil
2018-08-30 14:55:42 +00:00
}
type Type interface {
2018-10-23 12:38:10 +00:00
GetType() string
GetClass() string
2018-08-30 14:55:42 +00:00
}